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 {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792         if (this.over === false && !this.parent()) {
19793             return; 
19794         }
19795         if (this.triggers === false) {
19796             return;
19797         }
19798          
19799         // support parent
19800         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19801         var triggers = this.trigger ? this.trigger.split(' ') : [];
19802         Roo.each(triggers, function(trigger) {
19803         
19804             if (trigger == 'click') {
19805                 on_el.on('click', this.toggle, this);
19806             } else if (trigger != 'manual') {
19807                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19808                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19809       
19810                 on_el.on(eventIn  ,this.enter, this);
19811                 on_el.on(eventOut, this.leave, this);
19812             }
19813         }, this);
19814         
19815     },
19816     
19817     
19818     // private
19819     timeout : null,
19820     hoverState : null,
19821     
19822     toggle : function () {
19823         this.hoverState == 'in' ? this.leave() : this.enter();
19824     },
19825     
19826     enter : function () {
19827         
19828         clearTimeout(this.timeout);
19829     
19830         this.hoverState = 'in';
19831     
19832         if (!this.delay || !this.delay.show) {
19833             this.show();
19834             return;
19835         }
19836         var _t = this;
19837         this.timeout = setTimeout(function () {
19838             if (_t.hoverState == 'in') {
19839                 _t.show();
19840             }
19841         }, this.delay.show)
19842     },
19843     
19844     leave : function() {
19845         clearTimeout(this.timeout);
19846     
19847         this.hoverState = 'out';
19848     
19849         if (!this.delay || !this.delay.hide) {
19850             this.hide();
19851             return;
19852         }
19853         var _t = this;
19854         this.timeout = setTimeout(function () {
19855             if (_t.hoverState == 'out') {
19856                 _t.hide();
19857             }
19858         }, this.delay.hide)
19859     },
19860     /**
19861      * Show the popover
19862      * @param {Roo.Element|string|false} - element to align and point to. (set align to [ pos, offset ])
19863      */
19864     show : function (on_el)
19865     {
19866         
19867         on_el = on_el || false; // default to false
19868         var align = on_el && typeof(on_el._align) != 'undefined' ? on_el._align : false;
19869         
19870         if (!on_el) {
19871             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19872                 on_el = this.parent().el;
19873             } else if (this.over) {
19874                 Roo.get(this.over);
19875             }
19876             
19877         }
19878         
19879         if (!this.el) {
19880             this.render(document.body);
19881         }
19882         
19883         
19884         this.el.removeClass([
19885             'fade','top','bottom', 'left', 'right','in',
19886             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19887         ]);
19888         
19889         if (this.title === false) {
19890             this.headerEl.hide();
19891         }
19892         
19893         
19894         var placement = typeof this.placement == 'function' ?
19895             this.placement.call(this, this.el, on_el) :
19896             this.placement;
19897             
19898         /*
19899         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19900         
19901         // I think  'auto right' - but 
19902         
19903         var autoPlace = autoToken.test(placement);
19904         if (autoPlace) {
19905             placement = placement.replace(autoToken, '') || 'top';
19906         }
19907         */
19908         
19909         
19910         this.el.show();
19911         this.el.dom.style.display='block';
19912         
19913         //this.el.appendTo(on_el);
19914         
19915         var p = this.getPosition();
19916         var box = this.el.getBox();
19917         
19918         
19919         align = align || Roo.bootstrap.Popover.alignment[placement];
19920         this.el.addClass(align[2]);
19921
19922 //        Roo.log(align);
19923
19924         if (on_el) {
19925             this.el.alignTo(on_el, align[0],align[1]);
19926         } else {
19927             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19928             var es = this.el.getSize();
19929             var x = Roo.lib.Dom.getViewWidth()/2;
19930             var y = Roo.lib.Dom.getViewHeight()/2;
19931             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19932             
19933         }
19934
19935         
19936         //var arrow = this.el.select('.arrow',true).first();
19937         //arrow.set(align[2], 
19938         
19939         this.el.addClass('in');
19940         
19941         
19942         if (this.el.hasClass('fade')) {
19943             // fade it?
19944         }
19945         
19946         this.hoverState = 'in';
19947         
19948         if (this.modal) {
19949             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19950             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19951             this.maskEl.dom.style.display = 'block';
19952             this.maskEl.addClass('show');
19953         }
19954         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19955
19956         
19957         
19958         this.fireEvent('show', this);
19959         
19960     },
19961     hide : function()
19962     {
19963         this.el.setXY([0,0]);
19964         this.el.removeClass('in');
19965         this.el.hide();
19966         this.hoverState = null;
19967         this.maskEl.hide(); // always..
19968         this.fireEvent('hide', this);
19969     }
19970     
19971 });
19972
19973
19974 Roo.apply(Roo.bootstrap.Popover, {
19975
19976     alignment : {
19977         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19978         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19979         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19980         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19981     },
19982     
19983     zIndex : 20001,
19984
19985     clickHander : false,
19986     
19987
19988     onMouseDown : function(e)
19989     {
19990         if (!e.getTarget(".roo-popover")) {
19991             this.hideAll();
19992         }
19993          
19994     },
19995     
19996     popups : [],
19997     
19998     register : function(popup)
19999     {
20000         if (!Roo.bootstrap.Popover.clickHandler) {
20001             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20002         }
20003         // hide other popups.
20004         this.hideAll();
20005         this.popups.push(popup);
20006     },
20007     hideAll : function()
20008     {
20009         this.popups.forEach(function(p) {
20010             p.hide();
20011         });
20012     }
20013
20014 });/*
20015  * - LGPL
20016  *
20017  * Card header - holder for the card header elements.
20018  * 
20019  */
20020
20021 /**
20022  * @class Roo.bootstrap.PopoverNav
20023  * @extends Roo.bootstrap.NavGroup
20024  * Bootstrap Popover header navigation class
20025  * @constructor
20026  * Create a new Popover Header Navigation 
20027  * @param {Object} config The config object
20028  */
20029
20030 Roo.bootstrap.PopoverNav = function(config){
20031     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20032 };
20033
20034 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20035     
20036     
20037     container_method : 'getPopoverHeader' 
20038     
20039      
20040     
20041     
20042    
20043 });
20044
20045  
20046
20047  /*
20048  * - LGPL
20049  *
20050  * Progress
20051  * 
20052  */
20053
20054 /**
20055  * @class Roo.bootstrap.Progress
20056  * @extends Roo.bootstrap.Component
20057  * Bootstrap Progress class
20058  * @cfg {Boolean} striped striped of the progress bar
20059  * @cfg {Boolean} active animated of the progress bar
20060  * 
20061  * 
20062  * @constructor
20063  * Create a new Progress
20064  * @param {Object} config The config object
20065  */
20066
20067 Roo.bootstrap.Progress = function(config){
20068     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20069 };
20070
20071 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20072     
20073     striped : false,
20074     active: false,
20075     
20076     getAutoCreate : function(){
20077         var cfg = {
20078             tag: 'div',
20079             cls: 'progress'
20080         };
20081         
20082         
20083         if(this.striped){
20084             cfg.cls += ' progress-striped';
20085         }
20086       
20087         if(this.active){
20088             cfg.cls += ' active';
20089         }
20090         
20091         
20092         return cfg;
20093     }
20094    
20095 });
20096
20097  
20098
20099  /*
20100  * - LGPL
20101  *
20102  * ProgressBar
20103  * 
20104  */
20105
20106 /**
20107  * @class Roo.bootstrap.ProgressBar
20108  * @extends Roo.bootstrap.Component
20109  * Bootstrap ProgressBar class
20110  * @cfg {Number} aria_valuenow aria-value now
20111  * @cfg {Number} aria_valuemin aria-value min
20112  * @cfg {Number} aria_valuemax aria-value max
20113  * @cfg {String} label label for the progress bar
20114  * @cfg {String} panel (success | info | warning | danger )
20115  * @cfg {String} role role of the progress bar
20116  * @cfg {String} sr_only text
20117  * 
20118  * 
20119  * @constructor
20120  * Create a new ProgressBar
20121  * @param {Object} config The config object
20122  */
20123
20124 Roo.bootstrap.ProgressBar = function(config){
20125     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20126 };
20127
20128 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20129     
20130     aria_valuenow : 0,
20131     aria_valuemin : 0,
20132     aria_valuemax : 100,
20133     label : false,
20134     panel : false,
20135     role : false,
20136     sr_only: false,
20137     
20138     getAutoCreate : function()
20139     {
20140         
20141         var cfg = {
20142             tag: 'div',
20143             cls: 'progress-bar',
20144             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20145         };
20146         
20147         if(this.sr_only){
20148             cfg.cn = {
20149                 tag: 'span',
20150                 cls: 'sr-only',
20151                 html: this.sr_only
20152             }
20153         }
20154         
20155         if(this.role){
20156             cfg.role = this.role;
20157         }
20158         
20159         if(this.aria_valuenow){
20160             cfg['aria-valuenow'] = this.aria_valuenow;
20161         }
20162         
20163         if(this.aria_valuemin){
20164             cfg['aria-valuemin'] = this.aria_valuemin;
20165         }
20166         
20167         if(this.aria_valuemax){
20168             cfg['aria-valuemax'] = this.aria_valuemax;
20169         }
20170         
20171         if(this.label && !this.sr_only){
20172             cfg.html = this.label;
20173         }
20174         
20175         if(this.panel){
20176             cfg.cls += ' progress-bar-' + this.panel;
20177         }
20178         
20179         return cfg;
20180     },
20181     
20182     update : function(aria_valuenow)
20183     {
20184         this.aria_valuenow = aria_valuenow;
20185         
20186         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20187     }
20188    
20189 });
20190
20191  
20192
20193  /*
20194  * - LGPL
20195  *
20196  * column
20197  * 
20198  */
20199
20200 /**
20201  * @class Roo.bootstrap.TabGroup
20202  * @extends Roo.bootstrap.Column
20203  * Bootstrap Column class
20204  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20205  * @cfg {Boolean} carousel true to make the group behave like a carousel
20206  * @cfg {Boolean} bullets show bullets for the panels
20207  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20208  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20209  * @cfg {Boolean} showarrow (true|false) show arrow default true
20210  * 
20211  * @constructor
20212  * Create a new TabGroup
20213  * @param {Object} config The config object
20214  */
20215
20216 Roo.bootstrap.TabGroup = function(config){
20217     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20218     if (!this.navId) {
20219         this.navId = Roo.id();
20220     }
20221     this.tabs = [];
20222     Roo.bootstrap.TabGroup.register(this);
20223     
20224 };
20225
20226 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20227     
20228     carousel : false,
20229     transition : false,
20230     bullets : 0,
20231     timer : 0,
20232     autoslide : false,
20233     slideFn : false,
20234     slideOnTouch : false,
20235     showarrow : true,
20236     
20237     getAutoCreate : function()
20238     {
20239         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20240         
20241         cfg.cls += ' tab-content';
20242         
20243         if (this.carousel) {
20244             cfg.cls += ' carousel slide';
20245             
20246             cfg.cn = [{
20247                cls : 'carousel-inner',
20248                cn : []
20249             }];
20250         
20251             if(this.bullets  && !Roo.isTouch){
20252                 
20253                 var bullets = {
20254                     cls : 'carousel-bullets',
20255                     cn : []
20256                 };
20257                
20258                 if(this.bullets_cls){
20259                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20260                 }
20261                 
20262                 bullets.cn.push({
20263                     cls : 'clear'
20264                 });
20265                 
20266                 cfg.cn[0].cn.push(bullets);
20267             }
20268             
20269             if(this.showarrow){
20270                 cfg.cn[0].cn.push({
20271                     tag : 'div',
20272                     class : 'carousel-arrow',
20273                     cn : [
20274                         {
20275                             tag : 'div',
20276                             class : 'carousel-prev',
20277                             cn : [
20278                                 {
20279                                     tag : 'i',
20280                                     class : 'fa fa-chevron-left'
20281                                 }
20282                             ]
20283                         },
20284                         {
20285                             tag : 'div',
20286                             class : 'carousel-next',
20287                             cn : [
20288                                 {
20289                                     tag : 'i',
20290                                     class : 'fa fa-chevron-right'
20291                                 }
20292                             ]
20293                         }
20294                     ]
20295                 });
20296             }
20297             
20298         }
20299         
20300         return cfg;
20301     },
20302     
20303     initEvents:  function()
20304     {
20305 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20306 //            this.el.on("touchstart", this.onTouchStart, this);
20307 //        }
20308         
20309         if(this.autoslide){
20310             var _this = this;
20311             
20312             this.slideFn = window.setInterval(function() {
20313                 _this.showPanelNext();
20314             }, this.timer);
20315         }
20316         
20317         if(this.showarrow){
20318             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20319             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20320         }
20321         
20322         
20323     },
20324     
20325 //    onTouchStart : function(e, el, o)
20326 //    {
20327 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20328 //            return;
20329 //        }
20330 //        
20331 //        this.showPanelNext();
20332 //    },
20333     
20334     
20335     getChildContainer : function()
20336     {
20337         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20338     },
20339     
20340     /**
20341     * register a Navigation item
20342     * @param {Roo.bootstrap.NavItem} the navitem to add
20343     */
20344     register : function(item)
20345     {
20346         this.tabs.push( item);
20347         item.navId = this.navId; // not really needed..
20348         this.addBullet();
20349     
20350     },
20351     
20352     getActivePanel : function()
20353     {
20354         var r = false;
20355         Roo.each(this.tabs, function(t) {
20356             if (t.active) {
20357                 r = t;
20358                 return false;
20359             }
20360             return null;
20361         });
20362         return r;
20363         
20364     },
20365     getPanelByName : function(n)
20366     {
20367         var r = false;
20368         Roo.each(this.tabs, function(t) {
20369             if (t.tabId == n) {
20370                 r = t;
20371                 return false;
20372             }
20373             return null;
20374         });
20375         return r;
20376     },
20377     indexOfPanel : function(p)
20378     {
20379         var r = false;
20380         Roo.each(this.tabs, function(t,i) {
20381             if (t.tabId == p.tabId) {
20382                 r = i;
20383                 return false;
20384             }
20385             return null;
20386         });
20387         return r;
20388     },
20389     /**
20390      * show a specific panel
20391      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20392      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20393      */
20394     showPanel : function (pan)
20395     {
20396         if(this.transition || typeof(pan) == 'undefined'){
20397             Roo.log("waiting for the transitionend");
20398             return false;
20399         }
20400         
20401         if (typeof(pan) == 'number') {
20402             pan = this.tabs[pan];
20403         }
20404         
20405         if (typeof(pan) == 'string') {
20406             pan = this.getPanelByName(pan);
20407         }
20408         
20409         var cur = this.getActivePanel();
20410         
20411         if(!pan || !cur){
20412             Roo.log('pan or acitve pan is undefined');
20413             return false;
20414         }
20415         
20416         if (pan.tabId == this.getActivePanel().tabId) {
20417             return true;
20418         }
20419         
20420         if (false === cur.fireEvent('beforedeactivate')) {
20421             return false;
20422         }
20423         
20424         if(this.bullets > 0 && !Roo.isTouch){
20425             this.setActiveBullet(this.indexOfPanel(pan));
20426         }
20427         
20428         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20429             
20430             //class="carousel-item carousel-item-next carousel-item-left"
20431             
20432             this.transition = true;
20433             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20434             var lr = dir == 'next' ? 'left' : 'right';
20435             pan.el.addClass(dir); // or prev
20436             pan.el.addClass('carousel-item-' + dir); // or prev
20437             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20438             cur.el.addClass(lr); // or right
20439             pan.el.addClass(lr);
20440             cur.el.addClass('carousel-item-' +lr); // or right
20441             pan.el.addClass('carousel-item-' +lr);
20442             
20443             
20444             var _this = this;
20445             cur.el.on('transitionend', function() {
20446                 Roo.log("trans end?");
20447                 
20448                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20449                 pan.setActive(true);
20450                 
20451                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20452                 cur.setActive(false);
20453                 
20454                 _this.transition = false;
20455                 
20456             }, this, { single:  true } );
20457             
20458             return true;
20459         }
20460         
20461         cur.setActive(false);
20462         pan.setActive(true);
20463         
20464         return true;
20465         
20466     },
20467     showPanelNext : function()
20468     {
20469         var i = this.indexOfPanel(this.getActivePanel());
20470         
20471         if (i >= this.tabs.length - 1 && !this.autoslide) {
20472             return;
20473         }
20474         
20475         if (i >= this.tabs.length - 1 && this.autoslide) {
20476             i = -1;
20477         }
20478         
20479         this.showPanel(this.tabs[i+1]);
20480     },
20481     
20482     showPanelPrev : function()
20483     {
20484         var i = this.indexOfPanel(this.getActivePanel());
20485         
20486         if (i  < 1 && !this.autoslide) {
20487             return;
20488         }
20489         
20490         if (i < 1 && this.autoslide) {
20491             i = this.tabs.length;
20492         }
20493         
20494         this.showPanel(this.tabs[i-1]);
20495     },
20496     
20497     
20498     addBullet: function()
20499     {
20500         if(!this.bullets || Roo.isTouch){
20501             return;
20502         }
20503         var ctr = this.el.select('.carousel-bullets',true).first();
20504         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20505         var bullet = ctr.createChild({
20506             cls : 'bullet bullet-' + i
20507         },ctr.dom.lastChild);
20508         
20509         
20510         var _this = this;
20511         
20512         bullet.on('click', (function(e, el, o, ii, t){
20513
20514             e.preventDefault();
20515
20516             this.showPanel(ii);
20517
20518             if(this.autoslide && this.slideFn){
20519                 clearInterval(this.slideFn);
20520                 this.slideFn = window.setInterval(function() {
20521                     _this.showPanelNext();
20522                 }, this.timer);
20523             }
20524
20525         }).createDelegate(this, [i, bullet], true));
20526                 
20527         
20528     },
20529      
20530     setActiveBullet : function(i)
20531     {
20532         if(Roo.isTouch){
20533             return;
20534         }
20535         
20536         Roo.each(this.el.select('.bullet', true).elements, function(el){
20537             el.removeClass('selected');
20538         });
20539
20540         var bullet = this.el.select('.bullet-' + i, true).first();
20541         
20542         if(!bullet){
20543             return;
20544         }
20545         
20546         bullet.addClass('selected');
20547     }
20548     
20549     
20550   
20551 });
20552
20553  
20554
20555  
20556  
20557 Roo.apply(Roo.bootstrap.TabGroup, {
20558     
20559     groups: {},
20560      /**
20561     * register a Navigation Group
20562     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20563     */
20564     register : function(navgrp)
20565     {
20566         this.groups[navgrp.navId] = navgrp;
20567         
20568     },
20569     /**
20570     * fetch a Navigation Group based on the navigation ID
20571     * if one does not exist , it will get created.
20572     * @param {string} the navgroup to add
20573     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20574     */
20575     get: function(navId) {
20576         if (typeof(this.groups[navId]) == 'undefined') {
20577             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20578         }
20579         return this.groups[navId] ;
20580     }
20581     
20582     
20583     
20584 });
20585
20586  /*
20587  * - LGPL
20588  *
20589  * TabPanel
20590  * 
20591  */
20592
20593 /**
20594  * @class Roo.bootstrap.TabPanel
20595  * @extends Roo.bootstrap.Component
20596  * Bootstrap TabPanel class
20597  * @cfg {Boolean} active panel active
20598  * @cfg {String} html panel content
20599  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20600  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20601  * @cfg {String} href click to link..
20602  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20603  * 
20604  * 
20605  * @constructor
20606  * Create a new TabPanel
20607  * @param {Object} config The config object
20608  */
20609
20610 Roo.bootstrap.TabPanel = function(config){
20611     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20612     this.addEvents({
20613         /**
20614              * @event changed
20615              * Fires when the active status changes
20616              * @param {Roo.bootstrap.TabPanel} this
20617              * @param {Boolean} state the new state
20618             
20619          */
20620         'changed': true,
20621         /**
20622              * @event beforedeactivate
20623              * Fires before a tab is de-activated - can be used to do validation on a form.
20624              * @param {Roo.bootstrap.TabPanel} this
20625              * @return {Boolean} false if there is an error
20626             
20627          */
20628         'beforedeactivate': true
20629      });
20630     
20631     this.tabId = this.tabId || Roo.id();
20632   
20633 };
20634
20635 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20636     
20637     active: false,
20638     html: false,
20639     tabId: false,
20640     navId : false,
20641     href : '',
20642     touchSlide : false,
20643     getAutoCreate : function(){
20644         
20645         
20646         var cfg = {
20647             tag: 'div',
20648             // item is needed for carousel - not sure if it has any effect otherwise
20649             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20650             html: this.html || ''
20651         };
20652         
20653         if(this.active){
20654             cfg.cls += ' active';
20655         }
20656         
20657         if(this.tabId){
20658             cfg.tabId = this.tabId;
20659         }
20660         
20661         
20662         
20663         return cfg;
20664     },
20665     
20666     initEvents:  function()
20667     {
20668         var p = this.parent();
20669         
20670         this.navId = this.navId || p.navId;
20671         
20672         if (typeof(this.navId) != 'undefined') {
20673             // not really needed.. but just in case.. parent should be a NavGroup.
20674             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20675             
20676             tg.register(this);
20677             
20678             var i = tg.tabs.length - 1;
20679             
20680             if(this.active && tg.bullets > 0 && i < tg.bullets){
20681                 tg.setActiveBullet(i);
20682             }
20683         }
20684         
20685         this.el.on('click', this.onClick, this);
20686         
20687         if(Roo.isTouch && this.touchSlide){
20688             this.el.on("touchstart", this.onTouchStart, this);
20689             this.el.on("touchmove", this.onTouchMove, this);
20690             this.el.on("touchend", this.onTouchEnd, this);
20691         }
20692         
20693     },
20694     
20695     onRender : function(ct, position)
20696     {
20697         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20698     },
20699     
20700     setActive : function(state)
20701     {
20702         Roo.log("panel - set active " + this.tabId + "=" + state);
20703         
20704         this.active = state;
20705         if (!state) {
20706             this.el.removeClass('active');
20707             
20708         } else  if (!this.el.hasClass('active')) {
20709             this.el.addClass('active');
20710         }
20711         
20712         this.fireEvent('changed', this, state);
20713     },
20714     
20715     onClick : function(e)
20716     {
20717         e.preventDefault();
20718         
20719         if(!this.href.length){
20720             return;
20721         }
20722         
20723         window.location.href = this.href;
20724     },
20725     
20726     startX : 0,
20727     startY : 0,
20728     endX : 0,
20729     endY : 0,
20730     swiping : false,
20731     
20732     onTouchStart : function(e)
20733     {
20734         this.swiping = false;
20735         
20736         this.startX = e.browserEvent.touches[0].clientX;
20737         this.startY = e.browserEvent.touches[0].clientY;
20738     },
20739     
20740     onTouchMove : function(e)
20741     {
20742         this.swiping = true;
20743         
20744         this.endX = e.browserEvent.touches[0].clientX;
20745         this.endY = e.browserEvent.touches[0].clientY;
20746     },
20747     
20748     onTouchEnd : function(e)
20749     {
20750         if(!this.swiping){
20751             this.onClick(e);
20752             return;
20753         }
20754         
20755         var tabGroup = this.parent();
20756         
20757         if(this.endX > this.startX){ // swiping right
20758             tabGroup.showPanelPrev();
20759             return;
20760         }
20761         
20762         if(this.startX > this.endX){ // swiping left
20763             tabGroup.showPanelNext();
20764             return;
20765         }
20766     }
20767     
20768     
20769 });
20770  
20771
20772  
20773
20774  /*
20775  * - LGPL
20776  *
20777  * DateField
20778  * 
20779  */
20780
20781 /**
20782  * @class Roo.bootstrap.DateField
20783  * @extends Roo.bootstrap.Input
20784  * Bootstrap DateField class
20785  * @cfg {Number} weekStart default 0
20786  * @cfg {String} viewMode default empty, (months|years)
20787  * @cfg {String} minViewMode default empty, (months|years)
20788  * @cfg {Number} startDate default -Infinity
20789  * @cfg {Number} endDate default Infinity
20790  * @cfg {Boolean} todayHighlight default false
20791  * @cfg {Boolean} todayBtn default false
20792  * @cfg {Boolean} calendarWeeks default false
20793  * @cfg {Object} daysOfWeekDisabled default empty
20794  * @cfg {Boolean} singleMode default false (true | false)
20795  * 
20796  * @cfg {Boolean} keyboardNavigation default true
20797  * @cfg {String} language default en
20798  * 
20799  * @constructor
20800  * Create a new DateField
20801  * @param {Object} config The config object
20802  */
20803
20804 Roo.bootstrap.DateField = function(config){
20805     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20806      this.addEvents({
20807             /**
20808              * @event show
20809              * Fires when this field show.
20810              * @param {Roo.bootstrap.DateField} this
20811              * @param {Mixed} date The date value
20812              */
20813             show : true,
20814             /**
20815              * @event show
20816              * Fires when this field hide.
20817              * @param {Roo.bootstrap.DateField} this
20818              * @param {Mixed} date The date value
20819              */
20820             hide : true,
20821             /**
20822              * @event select
20823              * Fires when select a date.
20824              * @param {Roo.bootstrap.DateField} this
20825              * @param {Mixed} date The date value
20826              */
20827             select : true,
20828             /**
20829              * @event beforeselect
20830              * Fires when before select a date.
20831              * @param {Roo.bootstrap.DateField} this
20832              * @param {Mixed} date The date value
20833              */
20834             beforeselect : true
20835         });
20836 };
20837
20838 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20839     
20840     /**
20841      * @cfg {String} format
20842      * The default date format string which can be overriden for localization support.  The format must be
20843      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20844      */
20845     format : "m/d/y",
20846     /**
20847      * @cfg {String} altFormats
20848      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20849      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20850      */
20851     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20852     
20853     weekStart : 0,
20854     
20855     viewMode : '',
20856     
20857     minViewMode : '',
20858     
20859     todayHighlight : false,
20860     
20861     todayBtn: false,
20862     
20863     language: 'en',
20864     
20865     keyboardNavigation: true,
20866     
20867     calendarWeeks: false,
20868     
20869     startDate: -Infinity,
20870     
20871     endDate: Infinity,
20872     
20873     daysOfWeekDisabled: [],
20874     
20875     _events: [],
20876     
20877     singleMode : false,
20878     
20879     UTCDate: function()
20880     {
20881         return new Date(Date.UTC.apply(Date, arguments));
20882     },
20883     
20884     UTCToday: function()
20885     {
20886         var today = new Date();
20887         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20888     },
20889     
20890     getDate: function() {
20891             var d = this.getUTCDate();
20892             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20893     },
20894     
20895     getUTCDate: function() {
20896             return this.date;
20897     },
20898     
20899     setDate: function(d) {
20900             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20901     },
20902     
20903     setUTCDate: function(d) {
20904             this.date = d;
20905             this.setValue(this.formatDate(this.date));
20906     },
20907         
20908     onRender: function(ct, position)
20909     {
20910         
20911         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20912         
20913         this.language = this.language || 'en';
20914         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20915         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20916         
20917         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20918         this.format = this.format || 'm/d/y';
20919         this.isInline = false;
20920         this.isInput = true;
20921         this.component = this.el.select('.add-on', true).first() || false;
20922         this.component = (this.component && this.component.length === 0) ? false : this.component;
20923         this.hasInput = this.component && this.inputEl().length;
20924         
20925         if (typeof(this.minViewMode === 'string')) {
20926             switch (this.minViewMode) {
20927                 case 'months':
20928                     this.minViewMode = 1;
20929                     break;
20930                 case 'years':
20931                     this.minViewMode = 2;
20932                     break;
20933                 default:
20934                     this.minViewMode = 0;
20935                     break;
20936             }
20937         }
20938         
20939         if (typeof(this.viewMode === 'string')) {
20940             switch (this.viewMode) {
20941                 case 'months':
20942                     this.viewMode = 1;
20943                     break;
20944                 case 'years':
20945                     this.viewMode = 2;
20946                     break;
20947                 default:
20948                     this.viewMode = 0;
20949                     break;
20950             }
20951         }
20952                 
20953         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20954         
20955 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20956         
20957         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20958         
20959         this.picker().on('mousedown', this.onMousedown, this);
20960         this.picker().on('click', this.onClick, this);
20961         
20962         this.picker().addClass('datepicker-dropdown');
20963         
20964         this.startViewMode = this.viewMode;
20965         
20966         if(this.singleMode){
20967             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20968                 v.setVisibilityMode(Roo.Element.DISPLAY);
20969                 v.hide();
20970             });
20971             
20972             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20973                 v.setStyle('width', '189px');
20974             });
20975         }
20976         
20977         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20978             if(!this.calendarWeeks){
20979                 v.remove();
20980                 return;
20981             }
20982             
20983             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20984             v.attr('colspan', function(i, val){
20985                 return parseInt(val) + 1;
20986             });
20987         });
20988                         
20989         
20990         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20991         
20992         this.setStartDate(this.startDate);
20993         this.setEndDate(this.endDate);
20994         
20995         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20996         
20997         this.fillDow();
20998         this.fillMonths();
20999         this.update();
21000         this.showMode();
21001         
21002         if(this.isInline) {
21003             this.showPopup();
21004         }
21005     },
21006     
21007     picker : function()
21008     {
21009         return this.pickerEl;
21010 //        return this.el.select('.datepicker', true).first();
21011     },
21012     
21013     fillDow: function()
21014     {
21015         var dowCnt = this.weekStart;
21016         
21017         var dow = {
21018             tag: 'tr',
21019             cn: [
21020                 
21021             ]
21022         };
21023         
21024         if(this.calendarWeeks){
21025             dow.cn.push({
21026                 tag: 'th',
21027                 cls: 'cw',
21028                 html: '&nbsp;'
21029             })
21030         }
21031         
21032         while (dowCnt < this.weekStart + 7) {
21033             dow.cn.push({
21034                 tag: 'th',
21035                 cls: 'dow',
21036                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21037             });
21038         }
21039         
21040         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21041     },
21042     
21043     fillMonths: function()
21044     {    
21045         var i = 0;
21046         var months = this.picker().select('>.datepicker-months td', true).first();
21047         
21048         months.dom.innerHTML = '';
21049         
21050         while (i < 12) {
21051             var month = {
21052                 tag: 'span',
21053                 cls: 'month',
21054                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21055             };
21056             
21057             months.createChild(month);
21058         }
21059         
21060     },
21061     
21062     update: function()
21063     {
21064         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;
21065         
21066         if (this.date < this.startDate) {
21067             this.viewDate = new Date(this.startDate);
21068         } else if (this.date > this.endDate) {
21069             this.viewDate = new Date(this.endDate);
21070         } else {
21071             this.viewDate = new Date(this.date);
21072         }
21073         
21074         this.fill();
21075     },
21076     
21077     fill: function() 
21078     {
21079         var d = new Date(this.viewDate),
21080                 year = d.getUTCFullYear(),
21081                 month = d.getUTCMonth(),
21082                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21083                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21084                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21085                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21086                 currentDate = this.date && this.date.valueOf(),
21087                 today = this.UTCToday();
21088         
21089         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21090         
21091 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21092         
21093 //        this.picker.select('>tfoot th.today').
21094 //                                              .text(dates[this.language].today)
21095 //                                              .toggle(this.todayBtn !== false);
21096     
21097         this.updateNavArrows();
21098         this.fillMonths();
21099                                                 
21100         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21101         
21102         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21103          
21104         prevMonth.setUTCDate(day);
21105         
21106         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21107         
21108         var nextMonth = new Date(prevMonth);
21109         
21110         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21111         
21112         nextMonth = nextMonth.valueOf();
21113         
21114         var fillMonths = false;
21115         
21116         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21117         
21118         while(prevMonth.valueOf() <= nextMonth) {
21119             var clsName = '';
21120             
21121             if (prevMonth.getUTCDay() === this.weekStart) {
21122                 if(fillMonths){
21123                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21124                 }
21125                     
21126                 fillMonths = {
21127                     tag: 'tr',
21128                     cn: []
21129                 };
21130                 
21131                 if(this.calendarWeeks){
21132                     // ISO 8601: First week contains first thursday.
21133                     // ISO also states week starts on Monday, but we can be more abstract here.
21134                     var
21135                     // Start of current week: based on weekstart/current date
21136                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21137                     // Thursday of this week
21138                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21139                     // First Thursday of year, year from thursday
21140                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21141                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21142                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21143                     
21144                     fillMonths.cn.push({
21145                         tag: 'td',
21146                         cls: 'cw',
21147                         html: calWeek
21148                     });
21149                 }
21150             }
21151             
21152             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21153                 clsName += ' old';
21154             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21155                 clsName += ' new';
21156             }
21157             if (this.todayHighlight &&
21158                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21159                 prevMonth.getUTCMonth() == today.getMonth() &&
21160                 prevMonth.getUTCDate() == today.getDate()) {
21161                 clsName += ' today';
21162             }
21163             
21164             if (currentDate && prevMonth.valueOf() === currentDate) {
21165                 clsName += ' active';
21166             }
21167             
21168             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21169                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21170                     clsName += ' disabled';
21171             }
21172             
21173             fillMonths.cn.push({
21174                 tag: 'td',
21175                 cls: 'day ' + clsName,
21176                 html: prevMonth.getDate()
21177             });
21178             
21179             prevMonth.setDate(prevMonth.getDate()+1);
21180         }
21181           
21182         var currentYear = this.date && this.date.getUTCFullYear();
21183         var currentMonth = this.date && this.date.getUTCMonth();
21184         
21185         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21186         
21187         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21188             v.removeClass('active');
21189             
21190             if(currentYear === year && k === currentMonth){
21191                 v.addClass('active');
21192             }
21193             
21194             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21195                 v.addClass('disabled');
21196             }
21197             
21198         });
21199         
21200         
21201         year = parseInt(year/10, 10) * 10;
21202         
21203         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21204         
21205         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21206         
21207         year -= 1;
21208         for (var i = -1; i < 11; i++) {
21209             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21210                 tag: 'span',
21211                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21212                 html: year
21213             });
21214             
21215             year += 1;
21216         }
21217     },
21218     
21219     showMode: function(dir) 
21220     {
21221         if (dir) {
21222             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21223         }
21224         
21225         Roo.each(this.picker().select('>div',true).elements, function(v){
21226             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21227             v.hide();
21228         });
21229         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21230     },
21231     
21232     place: function()
21233     {
21234         if(this.isInline) {
21235             return;
21236         }
21237         
21238         this.picker().removeClass(['bottom', 'top']);
21239         
21240         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21241             /*
21242              * place to the top of element!
21243              *
21244              */
21245             
21246             this.picker().addClass('top');
21247             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21248             
21249             return;
21250         }
21251         
21252         this.picker().addClass('bottom');
21253         
21254         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21255     },
21256     
21257     parseDate : function(value)
21258     {
21259         if(!value || value instanceof Date){
21260             return value;
21261         }
21262         var v = Date.parseDate(value, this.format);
21263         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21264             v = Date.parseDate(value, 'Y-m-d');
21265         }
21266         if(!v && this.altFormats){
21267             if(!this.altFormatsArray){
21268                 this.altFormatsArray = this.altFormats.split("|");
21269             }
21270             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21271                 v = Date.parseDate(value, this.altFormatsArray[i]);
21272             }
21273         }
21274         return v;
21275     },
21276     
21277     formatDate : function(date, fmt)
21278     {   
21279         return (!date || !(date instanceof Date)) ?
21280         date : date.dateFormat(fmt || this.format);
21281     },
21282     
21283     onFocus : function()
21284     {
21285         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21286         this.showPopup();
21287     },
21288     
21289     onBlur : function()
21290     {
21291         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21292         
21293         var d = this.inputEl().getValue();
21294         
21295         this.setValue(d);
21296                 
21297         this.hidePopup();
21298     },
21299     
21300     showPopup : function()
21301     {
21302         this.picker().show();
21303         this.update();
21304         this.place();
21305         
21306         this.fireEvent('showpopup', this, this.date);
21307     },
21308     
21309     hidePopup : function()
21310     {
21311         if(this.isInline) {
21312             return;
21313         }
21314         this.picker().hide();
21315         this.viewMode = this.startViewMode;
21316         this.showMode();
21317         
21318         this.fireEvent('hidepopup', this, this.date);
21319         
21320     },
21321     
21322     onMousedown: function(e)
21323     {
21324         e.stopPropagation();
21325         e.preventDefault();
21326     },
21327     
21328     keyup: function(e)
21329     {
21330         Roo.bootstrap.DateField.superclass.keyup.call(this);
21331         this.update();
21332     },
21333
21334     setValue: function(v)
21335     {
21336         if(this.fireEvent('beforeselect', this, v) !== false){
21337             var d = new Date(this.parseDate(v) ).clearTime();
21338         
21339             if(isNaN(d.getTime())){
21340                 this.date = this.viewDate = '';
21341                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21342                 return;
21343             }
21344
21345             v = this.formatDate(d);
21346
21347             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21348
21349             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21350
21351             this.update();
21352
21353             this.fireEvent('select', this, this.date);
21354         }
21355     },
21356     
21357     getValue: function()
21358     {
21359         return this.formatDate(this.date);
21360     },
21361     
21362     fireKey: function(e)
21363     {
21364         if (!this.picker().isVisible()){
21365             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21366                 this.showPopup();
21367             }
21368             return;
21369         }
21370         
21371         var dateChanged = false,
21372         dir, day, month,
21373         newDate, newViewDate;
21374         
21375         switch(e.keyCode){
21376             case 27: // escape
21377                 this.hidePopup();
21378                 e.preventDefault();
21379                 break;
21380             case 37: // left
21381             case 39: // right
21382                 if (!this.keyboardNavigation) {
21383                     break;
21384                 }
21385                 dir = e.keyCode == 37 ? -1 : 1;
21386                 
21387                 if (e.ctrlKey){
21388                     newDate = this.moveYear(this.date, dir);
21389                     newViewDate = this.moveYear(this.viewDate, dir);
21390                 } else if (e.shiftKey){
21391                     newDate = this.moveMonth(this.date, dir);
21392                     newViewDate = this.moveMonth(this.viewDate, dir);
21393                 } else {
21394                     newDate = new Date(this.date);
21395                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21396                     newViewDate = new Date(this.viewDate);
21397                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21398                 }
21399                 if (this.dateWithinRange(newDate)){
21400                     this.date = newDate;
21401                     this.viewDate = newViewDate;
21402                     this.setValue(this.formatDate(this.date));
21403 //                    this.update();
21404                     e.preventDefault();
21405                     dateChanged = true;
21406                 }
21407                 break;
21408             case 38: // up
21409             case 40: // down
21410                 if (!this.keyboardNavigation) {
21411                     break;
21412                 }
21413                 dir = e.keyCode == 38 ? -1 : 1;
21414                 if (e.ctrlKey){
21415                     newDate = this.moveYear(this.date, dir);
21416                     newViewDate = this.moveYear(this.viewDate, dir);
21417                 } else if (e.shiftKey){
21418                     newDate = this.moveMonth(this.date, dir);
21419                     newViewDate = this.moveMonth(this.viewDate, dir);
21420                 } else {
21421                     newDate = new Date(this.date);
21422                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21423                     newViewDate = new Date(this.viewDate);
21424                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21425                 }
21426                 if (this.dateWithinRange(newDate)){
21427                     this.date = newDate;
21428                     this.viewDate = newViewDate;
21429                     this.setValue(this.formatDate(this.date));
21430 //                    this.update();
21431                     e.preventDefault();
21432                     dateChanged = true;
21433                 }
21434                 break;
21435             case 13: // enter
21436                 this.setValue(this.formatDate(this.date));
21437                 this.hidePopup();
21438                 e.preventDefault();
21439                 break;
21440             case 9: // tab
21441                 this.setValue(this.formatDate(this.date));
21442                 this.hidePopup();
21443                 break;
21444             case 16: // shift
21445             case 17: // ctrl
21446             case 18: // alt
21447                 break;
21448             default :
21449                 this.hidePopup();
21450                 
21451         }
21452     },
21453     
21454     
21455     onClick: function(e) 
21456     {
21457         e.stopPropagation();
21458         e.preventDefault();
21459         
21460         var target = e.getTarget();
21461         
21462         if(target.nodeName.toLowerCase() === 'i'){
21463             target = Roo.get(target).dom.parentNode;
21464         }
21465         
21466         var nodeName = target.nodeName;
21467         var className = target.className;
21468         var html = target.innerHTML;
21469         //Roo.log(nodeName);
21470         
21471         switch(nodeName.toLowerCase()) {
21472             case 'th':
21473                 switch(className) {
21474                     case 'switch':
21475                         this.showMode(1);
21476                         break;
21477                     case 'prev':
21478                     case 'next':
21479                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21480                         switch(this.viewMode){
21481                                 case 0:
21482                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21483                                         break;
21484                                 case 1:
21485                                 case 2:
21486                                         this.viewDate = this.moveYear(this.viewDate, dir);
21487                                         break;
21488                         }
21489                         this.fill();
21490                         break;
21491                     case 'today':
21492                         var date = new Date();
21493                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21494 //                        this.fill()
21495                         this.setValue(this.formatDate(this.date));
21496                         
21497                         this.hidePopup();
21498                         break;
21499                 }
21500                 break;
21501             case 'span':
21502                 if (className.indexOf('disabled') < 0) {
21503                     this.viewDate.setUTCDate(1);
21504                     if (className.indexOf('month') > -1) {
21505                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21506                     } else {
21507                         var year = parseInt(html, 10) || 0;
21508                         this.viewDate.setUTCFullYear(year);
21509                         
21510                     }
21511                     
21512                     if(this.singleMode){
21513                         this.setValue(this.formatDate(this.viewDate));
21514                         this.hidePopup();
21515                         return;
21516                     }
21517                     
21518                     this.showMode(-1);
21519                     this.fill();
21520                 }
21521                 break;
21522                 
21523             case 'td':
21524                 //Roo.log(className);
21525                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21526                     var day = parseInt(html, 10) || 1;
21527                     var year = this.viewDate.getUTCFullYear(),
21528                         month = this.viewDate.getUTCMonth();
21529
21530                     if (className.indexOf('old') > -1) {
21531                         if(month === 0 ){
21532                             month = 11;
21533                             year -= 1;
21534                         }else{
21535                             month -= 1;
21536                         }
21537                     } else if (className.indexOf('new') > -1) {
21538                         if (month == 11) {
21539                             month = 0;
21540                             year += 1;
21541                         } else {
21542                             month += 1;
21543                         }
21544                     }
21545                     //Roo.log([year,month,day]);
21546                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21547                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21548 //                    this.fill();
21549                     //Roo.log(this.formatDate(this.date));
21550                     this.setValue(this.formatDate(this.date));
21551                     this.hidePopup();
21552                 }
21553                 break;
21554         }
21555     },
21556     
21557     setStartDate: function(startDate)
21558     {
21559         this.startDate = startDate || -Infinity;
21560         if (this.startDate !== -Infinity) {
21561             this.startDate = this.parseDate(this.startDate);
21562         }
21563         this.update();
21564         this.updateNavArrows();
21565     },
21566
21567     setEndDate: function(endDate)
21568     {
21569         this.endDate = endDate || Infinity;
21570         if (this.endDate !== Infinity) {
21571             this.endDate = this.parseDate(this.endDate);
21572         }
21573         this.update();
21574         this.updateNavArrows();
21575     },
21576     
21577     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21578     {
21579         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21580         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21581             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21582         }
21583         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21584             return parseInt(d, 10);
21585         });
21586         this.update();
21587         this.updateNavArrows();
21588     },
21589     
21590     updateNavArrows: function() 
21591     {
21592         if(this.singleMode){
21593             return;
21594         }
21595         
21596         var d = new Date(this.viewDate),
21597         year = d.getUTCFullYear(),
21598         month = d.getUTCMonth();
21599         
21600         Roo.each(this.picker().select('.prev', true).elements, function(v){
21601             v.show();
21602             switch (this.viewMode) {
21603                 case 0:
21604
21605                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21606                         v.hide();
21607                     }
21608                     break;
21609                 case 1:
21610                 case 2:
21611                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21612                         v.hide();
21613                     }
21614                     break;
21615             }
21616         });
21617         
21618         Roo.each(this.picker().select('.next', true).elements, function(v){
21619             v.show();
21620             switch (this.viewMode) {
21621                 case 0:
21622
21623                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21624                         v.hide();
21625                     }
21626                     break;
21627                 case 1:
21628                 case 2:
21629                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21630                         v.hide();
21631                     }
21632                     break;
21633             }
21634         })
21635     },
21636     
21637     moveMonth: function(date, dir)
21638     {
21639         if (!dir) {
21640             return date;
21641         }
21642         var new_date = new Date(date.valueOf()),
21643         day = new_date.getUTCDate(),
21644         month = new_date.getUTCMonth(),
21645         mag = Math.abs(dir),
21646         new_month, test;
21647         dir = dir > 0 ? 1 : -1;
21648         if (mag == 1){
21649             test = dir == -1
21650             // If going back one month, make sure month is not current month
21651             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21652             ? function(){
21653                 return new_date.getUTCMonth() == month;
21654             }
21655             // If going forward one month, make sure month is as expected
21656             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21657             : function(){
21658                 return new_date.getUTCMonth() != new_month;
21659             };
21660             new_month = month + dir;
21661             new_date.setUTCMonth(new_month);
21662             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21663             if (new_month < 0 || new_month > 11) {
21664                 new_month = (new_month + 12) % 12;
21665             }
21666         } else {
21667             // For magnitudes >1, move one month at a time...
21668             for (var i=0; i<mag; i++) {
21669                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21670                 new_date = this.moveMonth(new_date, dir);
21671             }
21672             // ...then reset the day, keeping it in the new month
21673             new_month = new_date.getUTCMonth();
21674             new_date.setUTCDate(day);
21675             test = function(){
21676                 return new_month != new_date.getUTCMonth();
21677             };
21678         }
21679         // Common date-resetting loop -- if date is beyond end of month, make it
21680         // end of month
21681         while (test()){
21682             new_date.setUTCDate(--day);
21683             new_date.setUTCMonth(new_month);
21684         }
21685         return new_date;
21686     },
21687
21688     moveYear: function(date, dir)
21689     {
21690         return this.moveMonth(date, dir*12);
21691     },
21692
21693     dateWithinRange: function(date)
21694     {
21695         return date >= this.startDate && date <= this.endDate;
21696     },
21697
21698     
21699     remove: function() 
21700     {
21701         this.picker().remove();
21702     },
21703     
21704     validateValue : function(value)
21705     {
21706         if(this.getVisibilityEl().hasClass('hidden')){
21707             return true;
21708         }
21709         
21710         if(value.length < 1)  {
21711             if(this.allowBlank){
21712                 return true;
21713             }
21714             return false;
21715         }
21716         
21717         if(value.length < this.minLength){
21718             return false;
21719         }
21720         if(value.length > this.maxLength){
21721             return false;
21722         }
21723         if(this.vtype){
21724             var vt = Roo.form.VTypes;
21725             if(!vt[this.vtype](value, this)){
21726                 return false;
21727             }
21728         }
21729         if(typeof this.validator == "function"){
21730             var msg = this.validator(value);
21731             if(msg !== true){
21732                 return false;
21733             }
21734         }
21735         
21736         if(this.regex && !this.regex.test(value)){
21737             return false;
21738         }
21739         
21740         if(typeof(this.parseDate(value)) == 'undefined'){
21741             return false;
21742         }
21743         
21744         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21745             return false;
21746         }      
21747         
21748         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21749             return false;
21750         } 
21751         
21752         
21753         return true;
21754     },
21755     
21756     reset : function()
21757     {
21758         this.date = this.viewDate = '';
21759         
21760         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21761     }
21762    
21763 });
21764
21765 Roo.apply(Roo.bootstrap.DateField,  {
21766     
21767     head : {
21768         tag: 'thead',
21769         cn: [
21770         {
21771             tag: 'tr',
21772             cn: [
21773             {
21774                 tag: 'th',
21775                 cls: 'prev',
21776                 html: '<i class="fa fa-arrow-left"/>'
21777             },
21778             {
21779                 tag: 'th',
21780                 cls: 'switch',
21781                 colspan: '5'
21782             },
21783             {
21784                 tag: 'th',
21785                 cls: 'next',
21786                 html: '<i class="fa fa-arrow-right"/>'
21787             }
21788
21789             ]
21790         }
21791         ]
21792     },
21793     
21794     content : {
21795         tag: 'tbody',
21796         cn: [
21797         {
21798             tag: 'tr',
21799             cn: [
21800             {
21801                 tag: 'td',
21802                 colspan: '7'
21803             }
21804             ]
21805         }
21806         ]
21807     },
21808     
21809     footer : {
21810         tag: 'tfoot',
21811         cn: [
21812         {
21813             tag: 'tr',
21814             cn: [
21815             {
21816                 tag: 'th',
21817                 colspan: '7',
21818                 cls: 'today'
21819             }
21820                     
21821             ]
21822         }
21823         ]
21824     },
21825     
21826     dates:{
21827         en: {
21828             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21829             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21830             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21831             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21832             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21833             today: "Today"
21834         }
21835     },
21836     
21837     modes: [
21838     {
21839         clsName: 'days',
21840         navFnc: 'Month',
21841         navStep: 1
21842     },
21843     {
21844         clsName: 'months',
21845         navFnc: 'FullYear',
21846         navStep: 1
21847     },
21848     {
21849         clsName: 'years',
21850         navFnc: 'FullYear',
21851         navStep: 10
21852     }]
21853 });
21854
21855 Roo.apply(Roo.bootstrap.DateField,  {
21856   
21857     template : {
21858         tag: 'div',
21859         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21860         cn: [
21861         {
21862             tag: 'div',
21863             cls: 'datepicker-days',
21864             cn: [
21865             {
21866                 tag: 'table',
21867                 cls: 'table-condensed',
21868                 cn:[
21869                 Roo.bootstrap.DateField.head,
21870                 {
21871                     tag: 'tbody'
21872                 },
21873                 Roo.bootstrap.DateField.footer
21874                 ]
21875             }
21876             ]
21877         },
21878         {
21879             tag: 'div',
21880             cls: 'datepicker-months',
21881             cn: [
21882             {
21883                 tag: 'table',
21884                 cls: 'table-condensed',
21885                 cn:[
21886                 Roo.bootstrap.DateField.head,
21887                 Roo.bootstrap.DateField.content,
21888                 Roo.bootstrap.DateField.footer
21889                 ]
21890             }
21891             ]
21892         },
21893         {
21894             tag: 'div',
21895             cls: 'datepicker-years',
21896             cn: [
21897             {
21898                 tag: 'table',
21899                 cls: 'table-condensed',
21900                 cn:[
21901                 Roo.bootstrap.DateField.head,
21902                 Roo.bootstrap.DateField.content,
21903                 Roo.bootstrap.DateField.footer
21904                 ]
21905             }
21906             ]
21907         }
21908         ]
21909     }
21910 });
21911
21912  
21913
21914  /*
21915  * - LGPL
21916  *
21917  * TimeField
21918  * 
21919  */
21920
21921 /**
21922  * @class Roo.bootstrap.TimeField
21923  * @extends Roo.bootstrap.Input
21924  * Bootstrap DateField class
21925  * 
21926  * 
21927  * @constructor
21928  * Create a new TimeField
21929  * @param {Object} config The config object
21930  */
21931
21932 Roo.bootstrap.TimeField = function(config){
21933     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21934     this.addEvents({
21935             /**
21936              * @event show
21937              * Fires when this field show.
21938              * @param {Roo.bootstrap.DateField} thisthis
21939              * @param {Mixed} date The date value
21940              */
21941             show : true,
21942             /**
21943              * @event show
21944              * Fires when this field hide.
21945              * @param {Roo.bootstrap.DateField} this
21946              * @param {Mixed} date The date value
21947              */
21948             hide : true,
21949             /**
21950              * @event select
21951              * Fires when select a date.
21952              * @param {Roo.bootstrap.DateField} this
21953              * @param {Mixed} date The date value
21954              */
21955             select : true
21956         });
21957 };
21958
21959 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21960     
21961     /**
21962      * @cfg {String} format
21963      * The default time format string which can be overriden for localization support.  The format must be
21964      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21965      */
21966     format : "H:i",
21967
21968     getAutoCreate : function()
21969     {
21970         this.after = '<i class="fa far fa-clock"></i>';
21971         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21972         
21973          
21974     },
21975     onRender: function(ct, position)
21976     {
21977         
21978         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21979                 
21980         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21981         
21982         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21983         
21984         this.pop = this.picker().select('>.datepicker-time',true).first();
21985         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21986         
21987         this.picker().on('mousedown', this.onMousedown, this);
21988         this.picker().on('click', this.onClick, this);
21989         
21990         this.picker().addClass('datepicker-dropdown');
21991     
21992         this.fillTime();
21993         this.update();
21994             
21995         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
21996         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
21997         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21998         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21999         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22000         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22001
22002     },
22003     
22004     fireKey: function(e){
22005         if (!this.picker().isVisible()){
22006             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22007                 this.show();
22008             }
22009             return;
22010         }
22011
22012         e.preventDefault();
22013         
22014         switch(e.keyCode){
22015             case 27: // escape
22016                 this.hide();
22017                 break;
22018             case 37: // left
22019             case 39: // right
22020                 this.onTogglePeriod();
22021                 break;
22022             case 38: // up
22023                 this.onIncrementMinutes();
22024                 break;
22025             case 40: // down
22026                 this.onDecrementMinutes();
22027                 break;
22028             case 13: // enter
22029             case 9: // tab
22030                 this.setTime();
22031                 break;
22032         }
22033     },
22034     
22035     onClick: function(e) {
22036         e.stopPropagation();
22037         e.preventDefault();
22038     },
22039     
22040     picker : function()
22041     {
22042         return this.pickerEl;
22043     },
22044     
22045     fillTime: function()
22046     {    
22047         var time = this.pop.select('tbody', true).first();
22048         
22049         time.dom.innerHTML = '';
22050         
22051         time.createChild({
22052             tag: 'tr',
22053             cn: [
22054                 {
22055                     tag: 'td',
22056                     cn: [
22057                         {
22058                             tag: 'a',
22059                             href: '#',
22060                             cls: 'btn',
22061                             cn: [
22062                                 {
22063                                     tag: 'i',
22064                                     cls: 'hours-up fa fas fa-chevron-up'
22065                                 }
22066                             ]
22067                         } 
22068                     ]
22069                 },
22070                 {
22071                     tag: 'td',
22072                     cls: 'separator'
22073                 },
22074                 {
22075                     tag: 'td',
22076                     cn: [
22077                         {
22078                             tag: 'a',
22079                             href: '#',
22080                             cls: 'btn',
22081                             cn: [
22082                                 {
22083                                     tag: 'i',
22084                                     cls: 'minutes-up fa fas fa-chevron-up'
22085                                 }
22086                             ]
22087                         }
22088                     ]
22089                 },
22090                 {
22091                     tag: 'td',
22092                     cls: 'separator'
22093                 }
22094             ]
22095         });
22096         
22097         time.createChild({
22098             tag: 'tr',
22099             cn: [
22100                 {
22101                     tag: 'td',
22102                     cn: [
22103                         {
22104                             tag: 'span',
22105                             cls: 'timepicker-hour',
22106                             html: '00'
22107                         }  
22108                     ]
22109                 },
22110                 {
22111                     tag: 'td',
22112                     cls: 'separator',
22113                     html: ':'
22114                 },
22115                 {
22116                     tag: 'td',
22117                     cn: [
22118                         {
22119                             tag: 'span',
22120                             cls: 'timepicker-minute',
22121                             html: '00'
22122                         }  
22123                     ]
22124                 },
22125                 {
22126                     tag: 'td',
22127                     cls: 'separator'
22128                 },
22129                 {
22130                     tag: 'td',
22131                     cn: [
22132                         {
22133                             tag: 'button',
22134                             type: 'button',
22135                             cls: 'btn btn-primary period',
22136                             html: 'AM'
22137                             
22138                         }
22139                     ]
22140                 }
22141             ]
22142         });
22143         
22144         time.createChild({
22145             tag: 'tr',
22146             cn: [
22147                 {
22148                     tag: 'td',
22149                     cn: [
22150                         {
22151                             tag: 'a',
22152                             href: '#',
22153                             cls: 'btn',
22154                             cn: [
22155                                 {
22156                                     tag: 'span',
22157                                     cls: 'hours-down fa fas fa-chevron-down'
22158                                 }
22159                             ]
22160                         }
22161                     ]
22162                 },
22163                 {
22164                     tag: 'td',
22165                     cls: 'separator'
22166                 },
22167                 {
22168                     tag: 'td',
22169                     cn: [
22170                         {
22171                             tag: 'a',
22172                             href: '#',
22173                             cls: 'btn',
22174                             cn: [
22175                                 {
22176                                     tag: 'span',
22177                                     cls: 'minutes-down fa fas fa-chevron-down'
22178                                 }
22179                             ]
22180                         }
22181                     ]
22182                 },
22183                 {
22184                     tag: 'td',
22185                     cls: 'separator'
22186                 }
22187             ]
22188         });
22189         
22190     },
22191     
22192     update: function()
22193     {
22194         
22195         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22196         
22197         this.fill();
22198     },
22199     
22200     fill: function() 
22201     {
22202         var hours = this.time.getHours();
22203         var minutes = this.time.getMinutes();
22204         var period = 'AM';
22205         
22206         if(hours > 11){
22207             period = 'PM';
22208         }
22209         
22210         if(hours == 0){
22211             hours = 12;
22212         }
22213         
22214         
22215         if(hours > 12){
22216             hours = hours - 12;
22217         }
22218         
22219         if(hours < 10){
22220             hours = '0' + hours;
22221         }
22222         
22223         if(minutes < 10){
22224             minutes = '0' + minutes;
22225         }
22226         
22227         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22228         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22229         this.pop.select('button', true).first().dom.innerHTML = period;
22230         
22231     },
22232     
22233     place: function()
22234     {   
22235         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22236         
22237         var cls = ['bottom'];
22238         
22239         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22240             cls.pop();
22241             cls.push('top');
22242         }
22243         
22244         cls.push('right');
22245         
22246         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22247             cls.pop();
22248             cls.push('left');
22249         }
22250         //this.picker().setXY(20000,20000);
22251         this.picker().addClass(cls.join('-'));
22252         
22253         var _this = this;
22254         
22255         Roo.each(cls, function(c){
22256             if(c == 'bottom'){
22257                 (function() {
22258                  //  
22259                 }).defer(200);
22260                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22261                 //_this.picker().setTop(_this.inputEl().getHeight());
22262                 return;
22263             }
22264             if(c == 'top'){
22265                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22266                 
22267                 //_this.picker().setTop(0 - _this.picker().getHeight());
22268                 return;
22269             }
22270             /*
22271             if(c == 'left'){
22272                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22273                 return;
22274             }
22275             if(c == 'right'){
22276                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22277                 return;
22278             }
22279             */
22280         });
22281         
22282     },
22283   
22284     onFocus : function()
22285     {
22286         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22287         this.show();
22288     },
22289     
22290     onBlur : function()
22291     {
22292         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22293         this.hide();
22294     },
22295     
22296     show : function()
22297     {
22298         this.picker().show();
22299         this.pop.show();
22300         this.update();
22301         this.place();
22302         
22303         this.fireEvent('show', this, this.date);
22304     },
22305     
22306     hide : function()
22307     {
22308         this.picker().hide();
22309         this.pop.hide();
22310         
22311         this.fireEvent('hide', this, this.date);
22312     },
22313     
22314     setTime : function()
22315     {
22316         this.hide();
22317         this.setValue(this.time.format(this.format));
22318         
22319         this.fireEvent('select', this, this.date);
22320         
22321         
22322     },
22323     
22324     onMousedown: function(e){
22325         e.stopPropagation();
22326         e.preventDefault();
22327     },
22328     
22329     onIncrementHours: function()
22330     {
22331         Roo.log('onIncrementHours');
22332         this.time = this.time.add(Date.HOUR, 1);
22333         this.update();
22334         
22335     },
22336     
22337     onDecrementHours: function()
22338     {
22339         Roo.log('onDecrementHours');
22340         this.time = this.time.add(Date.HOUR, -1);
22341         this.update();
22342     },
22343     
22344     onIncrementMinutes: function()
22345     {
22346         Roo.log('onIncrementMinutes');
22347         this.time = this.time.add(Date.MINUTE, 1);
22348         this.update();
22349     },
22350     
22351     onDecrementMinutes: function()
22352     {
22353         Roo.log('onDecrementMinutes');
22354         this.time = this.time.add(Date.MINUTE, -1);
22355         this.update();
22356     },
22357     
22358     onTogglePeriod: function()
22359     {
22360         Roo.log('onTogglePeriod');
22361         this.time = this.time.add(Date.HOUR, 12);
22362         this.update();
22363     }
22364     
22365    
22366 });
22367  
22368
22369 Roo.apply(Roo.bootstrap.TimeField,  {
22370   
22371     template : {
22372         tag: 'div',
22373         cls: 'datepicker dropdown-menu',
22374         cn: [
22375             {
22376                 tag: 'div',
22377                 cls: 'datepicker-time',
22378                 cn: [
22379                 {
22380                     tag: 'table',
22381                     cls: 'table-condensed',
22382                     cn:[
22383                         {
22384                             tag: 'tbody',
22385                             cn: [
22386                                 {
22387                                     tag: 'tr',
22388                                     cn: [
22389                                     {
22390                                         tag: 'td',
22391                                         colspan: '7'
22392                                     }
22393                                     ]
22394                                 }
22395                             ]
22396                         },
22397                         {
22398                             tag: 'tfoot',
22399                             cn: [
22400                                 {
22401                                     tag: 'tr',
22402                                     cn: [
22403                                     {
22404                                         tag: 'th',
22405                                         colspan: '7',
22406                                         cls: '',
22407                                         cn: [
22408                                             {
22409                                                 tag: 'button',
22410                                                 cls: 'btn btn-info ok',
22411                                                 html: 'OK'
22412                                             }
22413                                         ]
22414                                     }
22415                     
22416                                     ]
22417                                 }
22418                             ]
22419                         }
22420                     ]
22421                 }
22422                 ]
22423             }
22424         ]
22425     }
22426 });
22427
22428  
22429
22430  /*
22431  * - LGPL
22432  *
22433  * MonthField
22434  * 
22435  */
22436
22437 /**
22438  * @class Roo.bootstrap.MonthField
22439  * @extends Roo.bootstrap.Input
22440  * Bootstrap MonthField class
22441  * 
22442  * @cfg {String} language default en
22443  * 
22444  * @constructor
22445  * Create a new MonthField
22446  * @param {Object} config The config object
22447  */
22448
22449 Roo.bootstrap.MonthField = function(config){
22450     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22451     
22452     this.addEvents({
22453         /**
22454          * @event show
22455          * Fires when this field show.
22456          * @param {Roo.bootstrap.MonthField} this
22457          * @param {Mixed} date The date value
22458          */
22459         show : true,
22460         /**
22461          * @event show
22462          * Fires when this field hide.
22463          * @param {Roo.bootstrap.MonthField} this
22464          * @param {Mixed} date The date value
22465          */
22466         hide : true,
22467         /**
22468          * @event select
22469          * Fires when select a date.
22470          * @param {Roo.bootstrap.MonthField} this
22471          * @param {String} oldvalue The old value
22472          * @param {String} newvalue The new value
22473          */
22474         select : true
22475     });
22476 };
22477
22478 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22479     
22480     onRender: function(ct, position)
22481     {
22482         
22483         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22484         
22485         this.language = this.language || 'en';
22486         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22487         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22488         
22489         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22490         this.isInline = false;
22491         this.isInput = true;
22492         this.component = this.el.select('.add-on', true).first() || false;
22493         this.component = (this.component && this.component.length === 0) ? false : this.component;
22494         this.hasInput = this.component && this.inputEL().length;
22495         
22496         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22497         
22498         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22499         
22500         this.picker().on('mousedown', this.onMousedown, this);
22501         this.picker().on('click', this.onClick, this);
22502         
22503         this.picker().addClass('datepicker-dropdown');
22504         
22505         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22506             v.setStyle('width', '189px');
22507         });
22508         
22509         this.fillMonths();
22510         
22511         this.update();
22512         
22513         if(this.isInline) {
22514             this.show();
22515         }
22516         
22517     },
22518     
22519     setValue: function(v, suppressEvent)
22520     {   
22521         var o = this.getValue();
22522         
22523         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22524         
22525         this.update();
22526
22527         if(suppressEvent !== true){
22528             this.fireEvent('select', this, o, v);
22529         }
22530         
22531     },
22532     
22533     getValue: function()
22534     {
22535         return this.value;
22536     },
22537     
22538     onClick: function(e) 
22539     {
22540         e.stopPropagation();
22541         e.preventDefault();
22542         
22543         var target = e.getTarget();
22544         
22545         if(target.nodeName.toLowerCase() === 'i'){
22546             target = Roo.get(target).dom.parentNode;
22547         }
22548         
22549         var nodeName = target.nodeName;
22550         var className = target.className;
22551         var html = target.innerHTML;
22552         
22553         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22554             return;
22555         }
22556         
22557         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22558         
22559         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22560         
22561         this.hide();
22562                         
22563     },
22564     
22565     picker : function()
22566     {
22567         return this.pickerEl;
22568     },
22569     
22570     fillMonths: function()
22571     {    
22572         var i = 0;
22573         var months = this.picker().select('>.datepicker-months td', true).first();
22574         
22575         months.dom.innerHTML = '';
22576         
22577         while (i < 12) {
22578             var month = {
22579                 tag: 'span',
22580                 cls: 'month',
22581                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22582             };
22583             
22584             months.createChild(month);
22585         }
22586         
22587     },
22588     
22589     update: function()
22590     {
22591         var _this = this;
22592         
22593         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22594             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22595         }
22596         
22597         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22598             e.removeClass('active');
22599             
22600             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22601                 e.addClass('active');
22602             }
22603         })
22604     },
22605     
22606     place: function()
22607     {
22608         if(this.isInline) {
22609             return;
22610         }
22611         
22612         this.picker().removeClass(['bottom', 'top']);
22613         
22614         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22615             /*
22616              * place to the top of element!
22617              *
22618              */
22619             
22620             this.picker().addClass('top');
22621             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22622             
22623             return;
22624         }
22625         
22626         this.picker().addClass('bottom');
22627         
22628         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22629     },
22630     
22631     onFocus : function()
22632     {
22633         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22634         this.show();
22635     },
22636     
22637     onBlur : function()
22638     {
22639         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22640         
22641         var d = this.inputEl().getValue();
22642         
22643         this.setValue(d);
22644                 
22645         this.hide();
22646     },
22647     
22648     show : function()
22649     {
22650         this.picker().show();
22651         this.picker().select('>.datepicker-months', true).first().show();
22652         this.update();
22653         this.place();
22654         
22655         this.fireEvent('show', this, this.date);
22656     },
22657     
22658     hide : function()
22659     {
22660         if(this.isInline) {
22661             return;
22662         }
22663         this.picker().hide();
22664         this.fireEvent('hide', this, this.date);
22665         
22666     },
22667     
22668     onMousedown: function(e)
22669     {
22670         e.stopPropagation();
22671         e.preventDefault();
22672     },
22673     
22674     keyup: function(e)
22675     {
22676         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22677         this.update();
22678     },
22679
22680     fireKey: function(e)
22681     {
22682         if (!this.picker().isVisible()){
22683             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22684                 this.show();
22685             }
22686             return;
22687         }
22688         
22689         var dir;
22690         
22691         switch(e.keyCode){
22692             case 27: // escape
22693                 this.hide();
22694                 e.preventDefault();
22695                 break;
22696             case 37: // left
22697             case 39: // right
22698                 dir = e.keyCode == 37 ? -1 : 1;
22699                 
22700                 this.vIndex = this.vIndex + dir;
22701                 
22702                 if(this.vIndex < 0){
22703                     this.vIndex = 0;
22704                 }
22705                 
22706                 if(this.vIndex > 11){
22707                     this.vIndex = 11;
22708                 }
22709                 
22710                 if(isNaN(this.vIndex)){
22711                     this.vIndex = 0;
22712                 }
22713                 
22714                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22715                 
22716                 break;
22717             case 38: // up
22718             case 40: // down
22719                 
22720                 dir = e.keyCode == 38 ? -1 : 1;
22721                 
22722                 this.vIndex = this.vIndex + dir * 4;
22723                 
22724                 if(this.vIndex < 0){
22725                     this.vIndex = 0;
22726                 }
22727                 
22728                 if(this.vIndex > 11){
22729                     this.vIndex = 11;
22730                 }
22731                 
22732                 if(isNaN(this.vIndex)){
22733                     this.vIndex = 0;
22734                 }
22735                 
22736                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22737                 break;
22738                 
22739             case 13: // enter
22740                 
22741                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22742                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22743                 }
22744                 
22745                 this.hide();
22746                 e.preventDefault();
22747                 break;
22748             case 9: // tab
22749                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22750                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22751                 }
22752                 this.hide();
22753                 break;
22754             case 16: // shift
22755             case 17: // ctrl
22756             case 18: // alt
22757                 break;
22758             default :
22759                 this.hide();
22760                 
22761         }
22762     },
22763     
22764     remove: function() 
22765     {
22766         this.picker().remove();
22767     }
22768    
22769 });
22770
22771 Roo.apply(Roo.bootstrap.MonthField,  {
22772     
22773     content : {
22774         tag: 'tbody',
22775         cn: [
22776         {
22777             tag: 'tr',
22778             cn: [
22779             {
22780                 tag: 'td',
22781                 colspan: '7'
22782             }
22783             ]
22784         }
22785         ]
22786     },
22787     
22788     dates:{
22789         en: {
22790             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22791             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22792         }
22793     }
22794 });
22795
22796 Roo.apply(Roo.bootstrap.MonthField,  {
22797   
22798     template : {
22799         tag: 'div',
22800         cls: 'datepicker dropdown-menu roo-dynamic',
22801         cn: [
22802             {
22803                 tag: 'div',
22804                 cls: 'datepicker-months',
22805                 cn: [
22806                 {
22807                     tag: 'table',
22808                     cls: 'table-condensed',
22809                     cn:[
22810                         Roo.bootstrap.DateField.content
22811                     ]
22812                 }
22813                 ]
22814             }
22815         ]
22816     }
22817 });
22818
22819  
22820
22821  
22822  /*
22823  * - LGPL
22824  *
22825  * CheckBox
22826  * 
22827  */
22828
22829 /**
22830  * @class Roo.bootstrap.CheckBox
22831  * @extends Roo.bootstrap.Input
22832  * Bootstrap CheckBox class
22833  * 
22834  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22835  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22836  * @cfg {String} boxLabel The text that appears beside the checkbox
22837  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22838  * @cfg {Boolean} checked initnal the element
22839  * @cfg {Boolean} inline inline the element (default false)
22840  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22841  * @cfg {String} tooltip label tooltip
22842  * 
22843  * @constructor
22844  * Create a new CheckBox
22845  * @param {Object} config The config object
22846  */
22847
22848 Roo.bootstrap.CheckBox = function(config){
22849     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22850    
22851     this.addEvents({
22852         /**
22853         * @event check
22854         * Fires when the element is checked or unchecked.
22855         * @param {Roo.bootstrap.CheckBox} this This input
22856         * @param {Boolean} checked The new checked value
22857         */
22858        check : true,
22859        /**
22860         * @event click
22861         * Fires when the element is click.
22862         * @param {Roo.bootstrap.CheckBox} this This input
22863         */
22864        click : true
22865     });
22866     
22867 };
22868
22869 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22870   
22871     inputType: 'checkbox',
22872     inputValue: 1,
22873     valueOff: 0,
22874     boxLabel: false,
22875     checked: false,
22876     weight : false,
22877     inline: false,
22878     tooltip : '',
22879     
22880     // checkbox success does not make any sense really.. 
22881     invalidClass : "",
22882     validClass : "",
22883     
22884     
22885     getAutoCreate : function()
22886     {
22887         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22888         
22889         var id = Roo.id();
22890         
22891         var cfg = {};
22892         
22893         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22894         
22895         if(this.inline){
22896             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22897         }
22898         
22899         var input =  {
22900             tag: 'input',
22901             id : id,
22902             type : this.inputType,
22903             value : this.inputValue,
22904             cls : 'roo-' + this.inputType, //'form-box',
22905             placeholder : this.placeholder || ''
22906             
22907         };
22908         
22909         if(this.inputType != 'radio'){
22910             var hidden =  {
22911                 tag: 'input',
22912                 type : 'hidden',
22913                 cls : 'roo-hidden-value',
22914                 value : this.checked ? this.inputValue : this.valueOff
22915             };
22916         }
22917         
22918             
22919         if (this.weight) { // Validity check?
22920             cfg.cls += " " + this.inputType + "-" + this.weight;
22921         }
22922         
22923         if (this.disabled) {
22924             input.disabled=true;
22925         }
22926         
22927         if(this.checked){
22928             input.checked = this.checked;
22929         }
22930         
22931         if (this.name) {
22932             
22933             input.name = this.name;
22934             
22935             if(this.inputType != 'radio'){
22936                 hidden.name = this.name;
22937                 input.name = '_hidden_' + this.name;
22938             }
22939         }
22940         
22941         if (this.size) {
22942             input.cls += ' input-' + this.size;
22943         }
22944         
22945         var settings=this;
22946         
22947         ['xs','sm','md','lg'].map(function(size){
22948             if (settings[size]) {
22949                 cfg.cls += ' col-' + size + '-' + settings[size];
22950             }
22951         });
22952         
22953         var inputblock = input;
22954          
22955         if (this.before || this.after) {
22956             
22957             inputblock = {
22958                 cls : 'input-group',
22959                 cn :  [] 
22960             };
22961             
22962             if (this.before) {
22963                 inputblock.cn.push({
22964                     tag :'span',
22965                     cls : 'input-group-addon',
22966                     html : this.before
22967                 });
22968             }
22969             
22970             inputblock.cn.push(input);
22971             
22972             if(this.inputType != 'radio'){
22973                 inputblock.cn.push(hidden);
22974             }
22975             
22976             if (this.after) {
22977                 inputblock.cn.push({
22978                     tag :'span',
22979                     cls : 'input-group-addon',
22980                     html : this.after
22981                 });
22982             }
22983             
22984         }
22985         var boxLabelCfg = false;
22986         
22987         if(this.boxLabel){
22988            
22989             boxLabelCfg = {
22990                 tag: 'label',
22991                 //'for': id, // box label is handled by onclick - so no for...
22992                 cls: 'box-label',
22993                 html: this.boxLabel
22994             };
22995             if(this.tooltip){
22996                 boxLabelCfg.tooltip = this.tooltip;
22997             }
22998              
22999         }
23000         
23001         
23002         if (align ==='left' && this.fieldLabel.length) {
23003 //                Roo.log("left and has label");
23004             cfg.cn = [
23005                 {
23006                     tag: 'label',
23007                     'for' :  id,
23008                     cls : 'control-label',
23009                     html : this.fieldLabel
23010                 },
23011                 {
23012                     cls : "", 
23013                     cn: [
23014                         inputblock
23015                     ]
23016                 }
23017             ];
23018             
23019             if (boxLabelCfg) {
23020                 cfg.cn[1].cn.push(boxLabelCfg);
23021             }
23022             
23023             if(this.labelWidth > 12){
23024                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23025             }
23026             
23027             if(this.labelWidth < 13 && this.labelmd == 0){
23028                 this.labelmd = this.labelWidth;
23029             }
23030             
23031             if(this.labellg > 0){
23032                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23033                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23034             }
23035             
23036             if(this.labelmd > 0){
23037                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23038                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23039             }
23040             
23041             if(this.labelsm > 0){
23042                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23043                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23044             }
23045             
23046             if(this.labelxs > 0){
23047                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23048                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23049             }
23050             
23051         } else if ( this.fieldLabel.length) {
23052 //                Roo.log(" label");
23053                 cfg.cn = [
23054                    
23055                     {
23056                         tag: this.boxLabel ? 'span' : 'label',
23057                         'for': id,
23058                         cls: 'control-label box-input-label',
23059                         //cls : 'input-group-addon',
23060                         html : this.fieldLabel
23061                     },
23062                     
23063                     inputblock
23064                     
23065                 ];
23066                 if (boxLabelCfg) {
23067                     cfg.cn.push(boxLabelCfg);
23068                 }
23069
23070         } else {
23071             
23072 //                Roo.log(" no label && no align");
23073                 cfg.cn = [  inputblock ] ;
23074                 if (boxLabelCfg) {
23075                     cfg.cn.push(boxLabelCfg);
23076                 }
23077
23078                 
23079         }
23080         
23081        
23082         
23083         if(this.inputType != 'radio'){
23084             cfg.cn.push(hidden);
23085         }
23086         
23087         return cfg;
23088         
23089     },
23090     
23091     /**
23092      * return the real input element.
23093      */
23094     inputEl: function ()
23095     {
23096         return this.el.select('input.roo-' + this.inputType,true).first();
23097     },
23098     hiddenEl: function ()
23099     {
23100         return this.el.select('input.roo-hidden-value',true).first();
23101     },
23102     
23103     labelEl: function()
23104     {
23105         return this.el.select('label.control-label',true).first();
23106     },
23107     /* depricated... */
23108     
23109     label: function()
23110     {
23111         return this.labelEl();
23112     },
23113     
23114     boxLabelEl: function()
23115     {
23116         return this.el.select('label.box-label',true).first();
23117     },
23118     
23119     initEvents : function()
23120     {
23121 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23122         
23123         this.inputEl().on('click', this.onClick,  this);
23124         
23125         if (this.boxLabel) { 
23126             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23127         }
23128         
23129         this.startValue = this.getValue();
23130         
23131         if(this.groupId){
23132             Roo.bootstrap.CheckBox.register(this);
23133         }
23134     },
23135     
23136     onClick : function(e)
23137     {   
23138         if(this.fireEvent('click', this, e) !== false){
23139             this.setChecked(!this.checked);
23140         }
23141         
23142     },
23143     
23144     setChecked : function(state,suppressEvent)
23145     {
23146         this.startValue = this.getValue();
23147
23148         if(this.inputType == 'radio'){
23149             
23150             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23151                 e.dom.checked = false;
23152             });
23153             
23154             this.inputEl().dom.checked = true;
23155             
23156             this.inputEl().dom.value = this.inputValue;
23157             
23158             if(suppressEvent !== true){
23159                 this.fireEvent('check', this, true);
23160             }
23161             
23162             this.validate();
23163             
23164             return;
23165         }
23166         
23167         this.checked = state;
23168         
23169         this.inputEl().dom.checked = state;
23170         
23171         
23172         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23173         
23174         if(suppressEvent !== true){
23175             this.fireEvent('check', this, state);
23176         }
23177         
23178         this.validate();
23179     },
23180     
23181     getValue : function()
23182     {
23183         if(this.inputType == 'radio'){
23184             return this.getGroupValue();
23185         }
23186         
23187         return this.hiddenEl().dom.value;
23188         
23189     },
23190     
23191     getGroupValue : function()
23192     {
23193         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23194             return '';
23195         }
23196         
23197         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23198     },
23199     
23200     setValue : function(v,suppressEvent)
23201     {
23202         if(this.inputType == 'radio'){
23203             this.setGroupValue(v, suppressEvent);
23204             return;
23205         }
23206         
23207         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23208         
23209         this.validate();
23210     },
23211     
23212     setGroupValue : function(v, suppressEvent)
23213     {
23214         this.startValue = this.getValue();
23215         
23216         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23217             e.dom.checked = false;
23218             
23219             if(e.dom.value == v){
23220                 e.dom.checked = true;
23221             }
23222         });
23223         
23224         if(suppressEvent !== true){
23225             this.fireEvent('check', this, true);
23226         }
23227
23228         this.validate();
23229         
23230         return;
23231     },
23232     
23233     validate : function()
23234     {
23235         if(this.getVisibilityEl().hasClass('hidden')){
23236             return true;
23237         }
23238         
23239         if(
23240                 this.disabled || 
23241                 (this.inputType == 'radio' && this.validateRadio()) ||
23242                 (this.inputType == 'checkbox' && this.validateCheckbox())
23243         ){
23244             this.markValid();
23245             return true;
23246         }
23247         
23248         this.markInvalid();
23249         return false;
23250     },
23251     
23252     validateRadio : function()
23253     {
23254         if(this.getVisibilityEl().hasClass('hidden')){
23255             return true;
23256         }
23257         
23258         if(this.allowBlank){
23259             return true;
23260         }
23261         
23262         var valid = false;
23263         
23264         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23265             if(!e.dom.checked){
23266                 return;
23267             }
23268             
23269             valid = true;
23270             
23271             return false;
23272         });
23273         
23274         return valid;
23275     },
23276     
23277     validateCheckbox : function()
23278     {
23279         if(!this.groupId){
23280             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23281             //return (this.getValue() == this.inputValue) ? true : false;
23282         }
23283         
23284         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23285         
23286         if(!group){
23287             return false;
23288         }
23289         
23290         var r = false;
23291         
23292         for(var i in group){
23293             if(group[i].el.isVisible(true)){
23294                 r = false;
23295                 break;
23296             }
23297             
23298             r = true;
23299         }
23300         
23301         for(var i in group){
23302             if(r){
23303                 break;
23304             }
23305             
23306             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23307         }
23308         
23309         return r;
23310     },
23311     
23312     /**
23313      * Mark this field as valid
23314      */
23315     markValid : function()
23316     {
23317         var _this = this;
23318         
23319         this.fireEvent('valid', this);
23320         
23321         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23322         
23323         if(this.groupId){
23324             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23325         }
23326         
23327         if(label){
23328             label.markValid();
23329         }
23330
23331         if(this.inputType == 'radio'){
23332             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23333                 var fg = e.findParent('.form-group', false, true);
23334                 if (Roo.bootstrap.version == 3) {
23335                     fg.removeClass([_this.invalidClass, _this.validClass]);
23336                     fg.addClass(_this.validClass);
23337                 } else {
23338                     fg.removeClass(['is-valid', 'is-invalid']);
23339                     fg.addClass('is-valid');
23340                 }
23341             });
23342             
23343             return;
23344         }
23345
23346         if(!this.groupId){
23347             var fg = this.el.findParent('.form-group', false, true);
23348             if (Roo.bootstrap.version == 3) {
23349                 fg.removeClass([this.invalidClass, this.validClass]);
23350                 fg.addClass(this.validClass);
23351             } else {
23352                 fg.removeClass(['is-valid', 'is-invalid']);
23353                 fg.addClass('is-valid');
23354             }
23355             return;
23356         }
23357         
23358         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23359         
23360         if(!group){
23361             return;
23362         }
23363         
23364         for(var i in group){
23365             var fg = group[i].el.findParent('.form-group', false, true);
23366             if (Roo.bootstrap.version == 3) {
23367                 fg.removeClass([this.invalidClass, this.validClass]);
23368                 fg.addClass(this.validClass);
23369             } else {
23370                 fg.removeClass(['is-valid', 'is-invalid']);
23371                 fg.addClass('is-valid');
23372             }
23373         }
23374     },
23375     
23376      /**
23377      * Mark this field as invalid
23378      * @param {String} msg The validation message
23379      */
23380     markInvalid : function(msg)
23381     {
23382         if(this.allowBlank){
23383             return;
23384         }
23385         
23386         var _this = this;
23387         
23388         this.fireEvent('invalid', this, msg);
23389         
23390         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23391         
23392         if(this.groupId){
23393             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23394         }
23395         
23396         if(label){
23397             label.markInvalid();
23398         }
23399             
23400         if(this.inputType == 'radio'){
23401             
23402             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23403                 var fg = e.findParent('.form-group', false, true);
23404                 if (Roo.bootstrap.version == 3) {
23405                     fg.removeClass([_this.invalidClass, _this.validClass]);
23406                     fg.addClass(_this.invalidClass);
23407                 } else {
23408                     fg.removeClass(['is-invalid', 'is-valid']);
23409                     fg.addClass('is-invalid');
23410                 }
23411             });
23412             
23413             return;
23414         }
23415         
23416         if(!this.groupId){
23417             var fg = this.el.findParent('.form-group', false, true);
23418             if (Roo.bootstrap.version == 3) {
23419                 fg.removeClass([_this.invalidClass, _this.validClass]);
23420                 fg.addClass(_this.invalidClass);
23421             } else {
23422                 fg.removeClass(['is-invalid', 'is-valid']);
23423                 fg.addClass('is-invalid');
23424             }
23425             return;
23426         }
23427         
23428         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23429         
23430         if(!group){
23431             return;
23432         }
23433         
23434         for(var i in group){
23435             var fg = group[i].el.findParent('.form-group', false, true);
23436             if (Roo.bootstrap.version == 3) {
23437                 fg.removeClass([_this.invalidClass, _this.validClass]);
23438                 fg.addClass(_this.invalidClass);
23439             } else {
23440                 fg.removeClass(['is-invalid', 'is-valid']);
23441                 fg.addClass('is-invalid');
23442             }
23443         }
23444         
23445     },
23446     
23447     clearInvalid : function()
23448     {
23449         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23450         
23451         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23452         
23453         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23454         
23455         if (label && label.iconEl) {
23456             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23457             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23458         }
23459     },
23460     
23461     disable : function()
23462     {
23463         if(this.inputType != 'radio'){
23464             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23465             return;
23466         }
23467         
23468         var _this = this;
23469         
23470         if(this.rendered){
23471             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23472                 _this.getActionEl().addClass(this.disabledClass);
23473                 e.dom.disabled = true;
23474             });
23475         }
23476         
23477         this.disabled = true;
23478         this.fireEvent("disable", this);
23479         return this;
23480     },
23481
23482     enable : function()
23483     {
23484         if(this.inputType != 'radio'){
23485             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23486             return;
23487         }
23488         
23489         var _this = this;
23490         
23491         if(this.rendered){
23492             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23493                 _this.getActionEl().removeClass(this.disabledClass);
23494                 e.dom.disabled = false;
23495             });
23496         }
23497         
23498         this.disabled = false;
23499         this.fireEvent("enable", this);
23500         return this;
23501     },
23502     
23503     setBoxLabel : function(v)
23504     {
23505         this.boxLabel = v;
23506         
23507         if(this.rendered){
23508             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23509         }
23510     }
23511
23512 });
23513
23514 Roo.apply(Roo.bootstrap.CheckBox, {
23515     
23516     groups: {},
23517     
23518      /**
23519     * register a CheckBox Group
23520     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23521     */
23522     register : function(checkbox)
23523     {
23524         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23525             this.groups[checkbox.groupId] = {};
23526         }
23527         
23528         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23529             return;
23530         }
23531         
23532         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23533         
23534     },
23535     /**
23536     * fetch a CheckBox Group based on the group ID
23537     * @param {string} the group ID
23538     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23539     */
23540     get: function(groupId) {
23541         if (typeof(this.groups[groupId]) == 'undefined') {
23542             return false;
23543         }
23544         
23545         return this.groups[groupId] ;
23546     }
23547     
23548     
23549 });
23550 /*
23551  * - LGPL
23552  *
23553  * RadioItem
23554  * 
23555  */
23556
23557 /**
23558  * @class Roo.bootstrap.Radio
23559  * @extends Roo.bootstrap.Component
23560  * Bootstrap Radio class
23561  * @cfg {String} boxLabel - the label associated
23562  * @cfg {String} value - the value of radio
23563  * 
23564  * @constructor
23565  * Create a new Radio
23566  * @param {Object} config The config object
23567  */
23568 Roo.bootstrap.Radio = function(config){
23569     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23570     
23571 };
23572
23573 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23574     
23575     boxLabel : '',
23576     
23577     value : '',
23578     
23579     getAutoCreate : function()
23580     {
23581         var cfg = {
23582             tag : 'div',
23583             cls : 'form-group radio',
23584             cn : [
23585                 {
23586                     tag : 'label',
23587                     cls : 'box-label',
23588                     html : this.boxLabel
23589                 }
23590             ]
23591         };
23592         
23593         return cfg;
23594     },
23595     
23596     initEvents : function() 
23597     {
23598         this.parent().register(this);
23599         
23600         this.el.on('click', this.onClick, this);
23601         
23602     },
23603     
23604     onClick : function(e)
23605     {
23606         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23607             this.setChecked(true);
23608         }
23609     },
23610     
23611     setChecked : function(state, suppressEvent)
23612     {
23613         this.parent().setValue(this.value, suppressEvent);
23614         
23615     },
23616     
23617     setBoxLabel : function(v)
23618     {
23619         this.boxLabel = v;
23620         
23621         if(this.rendered){
23622             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23623         }
23624     }
23625     
23626 });
23627  
23628
23629  /*
23630  * - LGPL
23631  *
23632  * Input
23633  * 
23634  */
23635
23636 /**
23637  * @class Roo.bootstrap.SecurePass
23638  * @extends Roo.bootstrap.Input
23639  * Bootstrap SecurePass class
23640  *
23641  * 
23642  * @constructor
23643  * Create a new SecurePass
23644  * @param {Object} config The config object
23645  */
23646  
23647 Roo.bootstrap.SecurePass = function (config) {
23648     // these go here, so the translation tool can replace them..
23649     this.errors = {
23650         PwdEmpty: "Please type a password, and then retype it to confirm.",
23651         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23652         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23653         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23654         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23655         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23656         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23657         TooWeak: "Your password is Too Weak."
23658     },
23659     this.meterLabel = "Password strength:";
23660     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23661     this.meterClass = [
23662         "roo-password-meter-tooweak", 
23663         "roo-password-meter-weak", 
23664         "roo-password-meter-medium", 
23665         "roo-password-meter-strong", 
23666         "roo-password-meter-grey"
23667     ];
23668     
23669     this.errors = {};
23670     
23671     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23672 }
23673
23674 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23675     /**
23676      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23677      * {
23678      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23679      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23680      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23681      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23682      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23683      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23684      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23685      * })
23686      */
23687     // private
23688     
23689     meterWidth: 300,
23690     errorMsg :'',    
23691     errors: false,
23692     imageRoot: '/',
23693     /**
23694      * @cfg {String/Object} Label for the strength meter (defaults to
23695      * 'Password strength:')
23696      */
23697     // private
23698     meterLabel: '',
23699     /**
23700      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23701      * ['Weak', 'Medium', 'Strong'])
23702      */
23703     // private    
23704     pwdStrengths: false,    
23705     // private
23706     strength: 0,
23707     // private
23708     _lastPwd: null,
23709     // private
23710     kCapitalLetter: 0,
23711     kSmallLetter: 1,
23712     kDigit: 2,
23713     kPunctuation: 3,
23714     
23715     insecure: false,
23716     // private
23717     initEvents: function ()
23718     {
23719         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23720
23721         if (this.el.is('input[type=password]') && Roo.isSafari) {
23722             this.el.on('keydown', this.SafariOnKeyDown, this);
23723         }
23724
23725         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23726     },
23727     // private
23728     onRender: function (ct, position)
23729     {
23730         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23731         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23732         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23733
23734         this.trigger.createChild({
23735                    cn: [
23736                     {
23737                     //id: 'PwdMeter',
23738                     tag: 'div',
23739                     cls: 'roo-password-meter-grey col-xs-12',
23740                     style: {
23741                         //width: 0,
23742                         //width: this.meterWidth + 'px'                                                
23743                         }
23744                     },
23745                     {                            
23746                          cls: 'roo-password-meter-text'                          
23747                     }
23748                 ]            
23749         });
23750
23751          
23752         if (this.hideTrigger) {
23753             this.trigger.setDisplayed(false);
23754         }
23755         this.setSize(this.width || '', this.height || '');
23756     },
23757     // private
23758     onDestroy: function ()
23759     {
23760         if (this.trigger) {
23761             this.trigger.removeAllListeners();
23762             this.trigger.remove();
23763         }
23764         if (this.wrap) {
23765             this.wrap.remove();
23766         }
23767         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23768     },
23769     // private
23770     checkStrength: function ()
23771     {
23772         var pwd = this.inputEl().getValue();
23773         if (pwd == this._lastPwd) {
23774             return;
23775         }
23776
23777         var strength;
23778         if (this.ClientSideStrongPassword(pwd)) {
23779             strength = 3;
23780         } else if (this.ClientSideMediumPassword(pwd)) {
23781             strength = 2;
23782         } else if (this.ClientSideWeakPassword(pwd)) {
23783             strength = 1;
23784         } else {
23785             strength = 0;
23786         }
23787         
23788         Roo.log('strength1: ' + strength);
23789         
23790         //var pm = this.trigger.child('div/div/div').dom;
23791         var pm = this.trigger.child('div/div');
23792         pm.removeClass(this.meterClass);
23793         pm.addClass(this.meterClass[strength]);
23794                 
23795         
23796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23797                 
23798         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23799         
23800         this._lastPwd = pwd;
23801     },
23802     reset: function ()
23803     {
23804         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23805         
23806         this._lastPwd = '';
23807         
23808         var pm = this.trigger.child('div/div');
23809         pm.removeClass(this.meterClass);
23810         pm.addClass('roo-password-meter-grey');        
23811         
23812         
23813         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23814         
23815         pt.innerHTML = '';
23816         this.inputEl().dom.type='password';
23817     },
23818     // private
23819     validateValue: function (value)
23820     {
23821         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23822             return false;
23823         }
23824         if (value.length == 0) {
23825             if (this.allowBlank) {
23826                 this.clearInvalid();
23827                 return true;
23828             }
23829
23830             this.markInvalid(this.errors.PwdEmpty);
23831             this.errorMsg = this.errors.PwdEmpty;
23832             return false;
23833         }
23834         
23835         if(this.insecure){
23836             return true;
23837         }
23838         
23839         if (!value.match(/[\x21-\x7e]+/)) {
23840             this.markInvalid(this.errors.PwdBadChar);
23841             this.errorMsg = this.errors.PwdBadChar;
23842             return false;
23843         }
23844         if (value.length < 6) {
23845             this.markInvalid(this.errors.PwdShort);
23846             this.errorMsg = this.errors.PwdShort;
23847             return false;
23848         }
23849         if (value.length > 16) {
23850             this.markInvalid(this.errors.PwdLong);
23851             this.errorMsg = this.errors.PwdLong;
23852             return false;
23853         }
23854         var strength;
23855         if (this.ClientSideStrongPassword(value)) {
23856             strength = 3;
23857         } else if (this.ClientSideMediumPassword(value)) {
23858             strength = 2;
23859         } else if (this.ClientSideWeakPassword(value)) {
23860             strength = 1;
23861         } else {
23862             strength = 0;
23863         }
23864
23865         
23866         if (strength < 2) {
23867             //this.markInvalid(this.errors.TooWeak);
23868             this.errorMsg = this.errors.TooWeak;
23869             //return false;
23870         }
23871         
23872         
23873         console.log('strength2: ' + strength);
23874         
23875         //var pm = this.trigger.child('div/div/div').dom;
23876         
23877         var pm = this.trigger.child('div/div');
23878         pm.removeClass(this.meterClass);
23879         pm.addClass(this.meterClass[strength]);
23880                 
23881         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23882                 
23883         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23884         
23885         this.errorMsg = ''; 
23886         return true;
23887     },
23888     // private
23889     CharacterSetChecks: function (type)
23890     {
23891         this.type = type;
23892         this.fResult = false;
23893     },
23894     // private
23895     isctype: function (character, type)
23896     {
23897         switch (type) {  
23898             case this.kCapitalLetter:
23899                 if (character >= 'A' && character <= 'Z') {
23900                     return true;
23901                 }
23902                 break;
23903             
23904             case this.kSmallLetter:
23905                 if (character >= 'a' && character <= 'z') {
23906                     return true;
23907                 }
23908                 break;
23909             
23910             case this.kDigit:
23911                 if (character >= '0' && character <= '9') {
23912                     return true;
23913                 }
23914                 break;
23915             
23916             case this.kPunctuation:
23917                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23918                     return true;
23919                 }
23920                 break;
23921             
23922             default:
23923                 return false;
23924         }
23925
23926     },
23927     // private
23928     IsLongEnough: function (pwd, size)
23929     {
23930         return !(pwd == null || isNaN(size) || pwd.length < size);
23931     },
23932     // private
23933     SpansEnoughCharacterSets: function (word, nb)
23934     {
23935         if (!this.IsLongEnough(word, nb))
23936         {
23937             return false;
23938         }
23939
23940         var characterSetChecks = new Array(
23941             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23942             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23943         );
23944         
23945         for (var index = 0; index < word.length; ++index) {
23946             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23947                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23948                     characterSetChecks[nCharSet].fResult = true;
23949                     break;
23950                 }
23951             }
23952         }
23953
23954         var nCharSets = 0;
23955         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23956             if (characterSetChecks[nCharSet].fResult) {
23957                 ++nCharSets;
23958             }
23959         }
23960
23961         if (nCharSets < nb) {
23962             return false;
23963         }
23964         return true;
23965     },
23966     // private
23967     ClientSideStrongPassword: function (pwd)
23968     {
23969         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23970     },
23971     // private
23972     ClientSideMediumPassword: function (pwd)
23973     {
23974         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23975     },
23976     // private
23977     ClientSideWeakPassword: function (pwd)
23978     {
23979         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23980     }
23981           
23982 })//<script type="text/javascript">
23983
23984 /*
23985  * Based  Ext JS Library 1.1.1
23986  * Copyright(c) 2006-2007, Ext JS, LLC.
23987  * LGPL
23988  *
23989  */
23990  
23991 /**
23992  * @class Roo.HtmlEditorCore
23993  * @extends Roo.Component
23994  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23995  *
23996  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23997  */
23998
23999 Roo.HtmlEditorCore = function(config){
24000     
24001     
24002     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24003     
24004     
24005     this.addEvents({
24006         /**
24007          * @event initialize
24008          * Fires when the editor is fully initialized (including the iframe)
24009          * @param {Roo.HtmlEditorCore} this
24010          */
24011         initialize: true,
24012         /**
24013          * @event activate
24014          * Fires when the editor is first receives the focus. Any insertion must wait
24015          * until after this event.
24016          * @param {Roo.HtmlEditorCore} this
24017          */
24018         activate: true,
24019          /**
24020          * @event beforesync
24021          * Fires before the textarea is updated with content from the editor iframe. Return false
24022          * to cancel the sync.
24023          * @param {Roo.HtmlEditorCore} this
24024          * @param {String} html
24025          */
24026         beforesync: true,
24027          /**
24028          * @event beforepush
24029          * Fires before the iframe editor is updated with content from the textarea. Return false
24030          * to cancel the push.
24031          * @param {Roo.HtmlEditorCore} this
24032          * @param {String} html
24033          */
24034         beforepush: true,
24035          /**
24036          * @event sync
24037          * Fires when the textarea is updated with content from the editor iframe.
24038          * @param {Roo.HtmlEditorCore} this
24039          * @param {String} html
24040          */
24041         sync: true,
24042          /**
24043          * @event push
24044          * Fires when the iframe editor is updated with content from the textarea.
24045          * @param {Roo.HtmlEditorCore} this
24046          * @param {String} html
24047          */
24048         push: true,
24049         
24050         /**
24051          * @event editorevent
24052          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24053          * @param {Roo.HtmlEditorCore} this
24054          */
24055         editorevent: true
24056         
24057     });
24058     
24059     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24060     
24061     // defaults : white / black...
24062     this.applyBlacklists();
24063     
24064     
24065     
24066 };
24067
24068
24069 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24070
24071
24072      /**
24073      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24074      */
24075     
24076     owner : false,
24077     
24078      /**
24079      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24080      *                        Roo.resizable.
24081      */
24082     resizable : false,
24083      /**
24084      * @cfg {Number} height (in pixels)
24085      */   
24086     height: 300,
24087    /**
24088      * @cfg {Number} width (in pixels)
24089      */   
24090     width: 500,
24091     
24092     /**
24093      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24094      * 
24095      */
24096     stylesheets: false,
24097     
24098     // id of frame..
24099     frameId: false,
24100     
24101     // private properties
24102     validationEvent : false,
24103     deferHeight: true,
24104     initialized : false,
24105     activated : false,
24106     sourceEditMode : false,
24107     onFocus : Roo.emptyFn,
24108     iframePad:3,
24109     hideMode:'offsets',
24110     
24111     clearUp: true,
24112     
24113     // blacklist + whitelisted elements..
24114     black: false,
24115     white: false,
24116      
24117     bodyCls : '',
24118
24119     /**
24120      * Protected method that will not generally be called directly. It
24121      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24122      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24123      */
24124     getDocMarkup : function(){
24125         // body styles..
24126         var st = '';
24127         
24128         // inherit styels from page...?? 
24129         if (this.stylesheets === false) {
24130             
24131             Roo.get(document.head).select('style').each(function(node) {
24132                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24133             });
24134             
24135             Roo.get(document.head).select('link').each(function(node) { 
24136                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24137             });
24138             
24139         } else if (!this.stylesheets.length) {
24140                 // simple..
24141                 st = '<style type="text/css">' +
24142                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24143                    '</style>';
24144         } else {
24145             for (var i in this.stylesheets) { 
24146                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24147             }
24148             
24149         }
24150         
24151         st +=  '<style type="text/css">' +
24152             'IMG { cursor: pointer } ' +
24153         '</style>';
24154
24155         var cls = 'roo-htmleditor-body';
24156         
24157         if(this.bodyCls.length){
24158             cls += ' ' + this.bodyCls;
24159         }
24160         
24161         return '<html><head>' + st  +
24162             //<style type="text/css">' +
24163             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24164             //'</style>' +
24165             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24166     },
24167
24168     // private
24169     onRender : function(ct, position)
24170     {
24171         var _t = this;
24172         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24173         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24174         
24175         
24176         this.el.dom.style.border = '0 none';
24177         this.el.dom.setAttribute('tabIndex', -1);
24178         this.el.addClass('x-hidden hide');
24179         
24180         
24181         
24182         if(Roo.isIE){ // fix IE 1px bogus margin
24183             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24184         }
24185        
24186         
24187         this.frameId = Roo.id();
24188         
24189          
24190         
24191         var iframe = this.owner.wrap.createChild({
24192             tag: 'iframe',
24193             cls: 'form-control', // bootstrap..
24194             id: this.frameId,
24195             name: this.frameId,
24196             frameBorder : 'no',
24197             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24198         }, this.el
24199         );
24200         
24201         
24202         this.iframe = iframe.dom;
24203
24204          this.assignDocWin();
24205         
24206         this.doc.designMode = 'on';
24207        
24208         this.doc.open();
24209         this.doc.write(this.getDocMarkup());
24210         this.doc.close();
24211
24212         
24213         var task = { // must defer to wait for browser to be ready
24214             run : function(){
24215                 //console.log("run task?" + this.doc.readyState);
24216                 this.assignDocWin();
24217                 if(this.doc.body || this.doc.readyState == 'complete'){
24218                     try {
24219                         this.doc.designMode="on";
24220                     } catch (e) {
24221                         return;
24222                     }
24223                     Roo.TaskMgr.stop(task);
24224                     this.initEditor.defer(10, this);
24225                 }
24226             },
24227             interval : 10,
24228             duration: 10000,
24229             scope: this
24230         };
24231         Roo.TaskMgr.start(task);
24232
24233     },
24234
24235     // private
24236     onResize : function(w, h)
24237     {
24238          Roo.log('resize: ' +w + ',' + h );
24239         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24240         if(!this.iframe){
24241             return;
24242         }
24243         if(typeof w == 'number'){
24244             
24245             this.iframe.style.width = w + 'px';
24246         }
24247         if(typeof h == 'number'){
24248             
24249             this.iframe.style.height = h + 'px';
24250             if(this.doc){
24251                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24252             }
24253         }
24254         
24255     },
24256
24257     /**
24258      * Toggles the editor between standard and source edit mode.
24259      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24260      */
24261     toggleSourceEdit : function(sourceEditMode){
24262         
24263         this.sourceEditMode = sourceEditMode === true;
24264         
24265         if(this.sourceEditMode){
24266  
24267             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24268             
24269         }else{
24270             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24271             //this.iframe.className = '';
24272             this.deferFocus();
24273         }
24274         //this.setSize(this.owner.wrap.getSize());
24275         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24276     },
24277
24278     
24279   
24280
24281     /**
24282      * Protected method that will not generally be called directly. If you need/want
24283      * custom HTML cleanup, this is the method you should override.
24284      * @param {String} html The HTML to be cleaned
24285      * return {String} The cleaned HTML
24286      */
24287     cleanHtml : function(html){
24288         html = String(html);
24289         if(html.length > 5){
24290             if(Roo.isSafari){ // strip safari nonsense
24291                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24292             }
24293         }
24294         if(html == '&nbsp;'){
24295             html = '';
24296         }
24297         return html;
24298     },
24299
24300     /**
24301      * HTML Editor -> Textarea
24302      * Protected method that will not generally be called directly. Syncs the contents
24303      * of the editor iframe with the textarea.
24304      */
24305     syncValue : function(){
24306         if(this.initialized){
24307             var bd = (this.doc.body || this.doc.documentElement);
24308             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24309             var html = bd.innerHTML;
24310             if(Roo.isSafari){
24311                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24312                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24313                 if(m && m[1]){
24314                     html = '<div style="'+m[0]+'">' + html + '</div>';
24315                 }
24316             }
24317             html = this.cleanHtml(html);
24318             // fix up the special chars.. normaly like back quotes in word...
24319             // however we do not want to do this with chinese..
24320             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24321                 
24322                 var cc = match.charCodeAt();
24323
24324                 // Get the character value, handling surrogate pairs
24325                 if (match.length == 2) {
24326                     // It's a surrogate pair, calculate the Unicode code point
24327                     var high = match.charCodeAt(0) - 0xD800;
24328                     var low  = match.charCodeAt(1) - 0xDC00;
24329                     cc = (high * 0x400) + low + 0x10000;
24330                 }  else if (
24331                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24332                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24333                     (cc >= 0xf900 && cc < 0xfb00 )
24334                 ) {
24335                         return match;
24336                 }  
24337          
24338                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24339                 return "&#" + cc + ";";
24340                 
24341                 
24342             });
24343             
24344             
24345              
24346             if(this.owner.fireEvent('beforesync', this, html) !== false){
24347                 this.el.dom.value = html;
24348                 this.owner.fireEvent('sync', this, html);
24349             }
24350         }
24351     },
24352
24353     /**
24354      * Protected method that will not generally be called directly. Pushes the value of the textarea
24355      * into the iframe editor.
24356      */
24357     pushValue : function(){
24358         if(this.initialized){
24359             var v = this.el.dom.value.trim();
24360             
24361 //            if(v.length < 1){
24362 //                v = '&#160;';
24363 //            }
24364             
24365             if(this.owner.fireEvent('beforepush', this, v) !== false){
24366                 var d = (this.doc.body || this.doc.documentElement);
24367                 d.innerHTML = v;
24368                 this.cleanUpPaste();
24369                 this.el.dom.value = d.innerHTML;
24370                 this.owner.fireEvent('push', this, v);
24371             }
24372         }
24373     },
24374
24375     // private
24376     deferFocus : function(){
24377         this.focus.defer(10, this);
24378     },
24379
24380     // doc'ed in Field
24381     focus : function(){
24382         if(this.win && !this.sourceEditMode){
24383             this.win.focus();
24384         }else{
24385             this.el.focus();
24386         }
24387     },
24388     
24389     assignDocWin: function()
24390     {
24391         var iframe = this.iframe;
24392         
24393          if(Roo.isIE){
24394             this.doc = iframe.contentWindow.document;
24395             this.win = iframe.contentWindow;
24396         } else {
24397 //            if (!Roo.get(this.frameId)) {
24398 //                return;
24399 //            }
24400 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24401 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24402             
24403             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24404                 return;
24405             }
24406             
24407             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24408             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24409         }
24410     },
24411     
24412     // private
24413     initEditor : function(){
24414         //console.log("INIT EDITOR");
24415         this.assignDocWin();
24416         
24417         
24418         
24419         this.doc.designMode="on";
24420         this.doc.open();
24421         this.doc.write(this.getDocMarkup());
24422         this.doc.close();
24423         
24424         var dbody = (this.doc.body || this.doc.documentElement);
24425         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24426         // this copies styles from the containing element into thsi one..
24427         // not sure why we need all of this..
24428         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24429         
24430         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24431         //ss['background-attachment'] = 'fixed'; // w3c
24432         dbody.bgProperties = 'fixed'; // ie
24433         //Roo.DomHelper.applyStyles(dbody, ss);
24434         Roo.EventManager.on(this.doc, {
24435             //'mousedown': this.onEditorEvent,
24436             'mouseup': this.onEditorEvent,
24437             'dblclick': this.onEditorEvent,
24438             'click': this.onEditorEvent,
24439             'keyup': this.onEditorEvent,
24440             buffer:100,
24441             scope: this
24442         });
24443         if(Roo.isGecko){
24444             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24445         }
24446         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24447             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24448         }
24449         this.initialized = true;
24450
24451         this.owner.fireEvent('initialize', this);
24452         this.pushValue();
24453     },
24454
24455     // private
24456     onDestroy : function(){
24457         
24458         
24459         
24460         if(this.rendered){
24461             
24462             //for (var i =0; i < this.toolbars.length;i++) {
24463             //    // fixme - ask toolbars for heights?
24464             //    this.toolbars[i].onDestroy();
24465            // }
24466             
24467             //this.wrap.dom.innerHTML = '';
24468             //this.wrap.remove();
24469         }
24470     },
24471
24472     // private
24473     onFirstFocus : function(){
24474         
24475         this.assignDocWin();
24476         
24477         
24478         this.activated = true;
24479          
24480     
24481         if(Roo.isGecko){ // prevent silly gecko errors
24482             this.win.focus();
24483             var s = this.win.getSelection();
24484             if(!s.focusNode || s.focusNode.nodeType != 3){
24485                 var r = s.getRangeAt(0);
24486                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24487                 r.collapse(true);
24488                 this.deferFocus();
24489             }
24490             try{
24491                 this.execCmd('useCSS', true);
24492                 this.execCmd('styleWithCSS', false);
24493             }catch(e){}
24494         }
24495         this.owner.fireEvent('activate', this);
24496     },
24497
24498     // private
24499     adjustFont: function(btn){
24500         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24501         //if(Roo.isSafari){ // safari
24502         //    adjust *= 2;
24503        // }
24504         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24505         if(Roo.isSafari){ // safari
24506             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24507             v =  (v < 10) ? 10 : v;
24508             v =  (v > 48) ? 48 : v;
24509             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24510             
24511         }
24512         
24513         
24514         v = Math.max(1, v+adjust);
24515         
24516         this.execCmd('FontSize', v  );
24517     },
24518
24519     onEditorEvent : function(e)
24520     {
24521         this.owner.fireEvent('editorevent', this, e);
24522       //  this.updateToolbar();
24523         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24524     },
24525
24526     insertTag : function(tg)
24527     {
24528         // could be a bit smarter... -> wrap the current selected tRoo..
24529         if (tg.toLowerCase() == 'span' ||
24530             tg.toLowerCase() == 'code' ||
24531             tg.toLowerCase() == 'sup' ||
24532             tg.toLowerCase() == 'sub' 
24533             ) {
24534             
24535             range = this.createRange(this.getSelection());
24536             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24537             wrappingNode.appendChild(range.extractContents());
24538             range.insertNode(wrappingNode);
24539
24540             return;
24541             
24542             
24543             
24544         }
24545         this.execCmd("formatblock",   tg);
24546         
24547     },
24548     
24549     insertText : function(txt)
24550     {
24551         
24552         
24553         var range = this.createRange();
24554         range.deleteContents();
24555                //alert(Sender.getAttribute('label'));
24556                
24557         range.insertNode(this.doc.createTextNode(txt));
24558     } ,
24559     
24560      
24561
24562     /**
24563      * Executes a Midas editor command on the editor document and performs necessary focus and
24564      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24565      * @param {String} cmd The Midas command
24566      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24567      */
24568     relayCmd : function(cmd, value){
24569         this.win.focus();
24570         this.execCmd(cmd, value);
24571         this.owner.fireEvent('editorevent', this);
24572         //this.updateToolbar();
24573         this.owner.deferFocus();
24574     },
24575
24576     /**
24577      * Executes a Midas editor command directly on the editor document.
24578      * For visual commands, you should use {@link #relayCmd} instead.
24579      * <b>This should only be called after the editor is initialized.</b>
24580      * @param {String} cmd The Midas command
24581      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24582      */
24583     execCmd : function(cmd, value){
24584         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24585         this.syncValue();
24586     },
24587  
24588  
24589    
24590     /**
24591      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24592      * to insert tRoo.
24593      * @param {String} text | dom node.. 
24594      */
24595     insertAtCursor : function(text)
24596     {
24597         
24598         if(!this.activated){
24599             return;
24600         }
24601         /*
24602         if(Roo.isIE){
24603             this.win.focus();
24604             var r = this.doc.selection.createRange();
24605             if(r){
24606                 r.collapse(true);
24607                 r.pasteHTML(text);
24608                 this.syncValue();
24609                 this.deferFocus();
24610             
24611             }
24612             return;
24613         }
24614         */
24615         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24616             this.win.focus();
24617             
24618             
24619             // from jquery ui (MIT licenced)
24620             var range, node;
24621             var win = this.win;
24622             
24623             if (win.getSelection && win.getSelection().getRangeAt) {
24624                 range = win.getSelection().getRangeAt(0);
24625                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24626                 range.insertNode(node);
24627             } else if (win.document.selection && win.document.selection.createRange) {
24628                 // no firefox support
24629                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24630                 win.document.selection.createRange().pasteHTML(txt);
24631             } else {
24632                 // no firefox support
24633                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24634                 this.execCmd('InsertHTML', txt);
24635             } 
24636             
24637             this.syncValue();
24638             
24639             this.deferFocus();
24640         }
24641     },
24642  // private
24643     mozKeyPress : function(e){
24644         if(e.ctrlKey){
24645             var c = e.getCharCode(), cmd;
24646           
24647             if(c > 0){
24648                 c = String.fromCharCode(c).toLowerCase();
24649                 switch(c){
24650                     case 'b':
24651                         cmd = 'bold';
24652                         break;
24653                     case 'i':
24654                         cmd = 'italic';
24655                         break;
24656                     
24657                     case 'u':
24658                         cmd = 'underline';
24659                         break;
24660                     
24661                     case 'v':
24662                         this.cleanUpPaste.defer(100, this);
24663                         return;
24664                         
24665                 }
24666                 if(cmd){
24667                     this.win.focus();
24668                     this.execCmd(cmd);
24669                     this.deferFocus();
24670                     e.preventDefault();
24671                 }
24672                 
24673             }
24674         }
24675     },
24676
24677     // private
24678     fixKeys : function(){ // load time branching for fastest keydown performance
24679         if(Roo.isIE){
24680             return function(e){
24681                 var k = e.getKey(), r;
24682                 if(k == e.TAB){
24683                     e.stopEvent();
24684                     r = this.doc.selection.createRange();
24685                     if(r){
24686                         r.collapse(true);
24687                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24688                         this.deferFocus();
24689                     }
24690                     return;
24691                 }
24692                 
24693                 if(k == e.ENTER){
24694                     r = this.doc.selection.createRange();
24695                     if(r){
24696                         var target = r.parentElement();
24697                         if(!target || target.tagName.toLowerCase() != 'li'){
24698                             e.stopEvent();
24699                             r.pasteHTML('<br />');
24700                             r.collapse(false);
24701                             r.select();
24702                         }
24703                     }
24704                 }
24705                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24706                     this.cleanUpPaste.defer(100, this);
24707                     return;
24708                 }
24709                 
24710                 
24711             };
24712         }else if(Roo.isOpera){
24713             return function(e){
24714                 var k = e.getKey();
24715                 if(k == e.TAB){
24716                     e.stopEvent();
24717                     this.win.focus();
24718                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24719                     this.deferFocus();
24720                 }
24721                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24722                     this.cleanUpPaste.defer(100, this);
24723                     return;
24724                 }
24725                 
24726             };
24727         }else if(Roo.isSafari){
24728             return function(e){
24729                 var k = e.getKey();
24730                 
24731                 if(k == e.TAB){
24732                     e.stopEvent();
24733                     this.execCmd('InsertText','\t');
24734                     this.deferFocus();
24735                     return;
24736                 }
24737                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24738                     this.cleanUpPaste.defer(100, this);
24739                     return;
24740                 }
24741                 
24742              };
24743         }
24744     }(),
24745     
24746     getAllAncestors: function()
24747     {
24748         var p = this.getSelectedNode();
24749         var a = [];
24750         if (!p) {
24751             a.push(p); // push blank onto stack..
24752             p = this.getParentElement();
24753         }
24754         
24755         
24756         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24757             a.push(p);
24758             p = p.parentNode;
24759         }
24760         a.push(this.doc.body);
24761         return a;
24762     },
24763     lastSel : false,
24764     lastSelNode : false,
24765     
24766     
24767     getSelection : function() 
24768     {
24769         this.assignDocWin();
24770         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24771     },
24772     
24773     getSelectedNode: function() 
24774     {
24775         // this may only work on Gecko!!!
24776         
24777         // should we cache this!!!!
24778         
24779         
24780         
24781          
24782         var range = this.createRange(this.getSelection()).cloneRange();
24783         
24784         if (Roo.isIE) {
24785             var parent = range.parentElement();
24786             while (true) {
24787                 var testRange = range.duplicate();
24788                 testRange.moveToElementText(parent);
24789                 if (testRange.inRange(range)) {
24790                     break;
24791                 }
24792                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24793                     break;
24794                 }
24795                 parent = parent.parentElement;
24796             }
24797             return parent;
24798         }
24799         
24800         // is ancestor a text element.
24801         var ac =  range.commonAncestorContainer;
24802         if (ac.nodeType == 3) {
24803             ac = ac.parentNode;
24804         }
24805         
24806         var ar = ac.childNodes;
24807          
24808         var nodes = [];
24809         var other_nodes = [];
24810         var has_other_nodes = false;
24811         for (var i=0;i<ar.length;i++) {
24812             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24813                 continue;
24814             }
24815             // fullly contained node.
24816             
24817             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24818                 nodes.push(ar[i]);
24819                 continue;
24820             }
24821             
24822             // probably selected..
24823             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24824                 other_nodes.push(ar[i]);
24825                 continue;
24826             }
24827             // outer..
24828             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24829                 continue;
24830             }
24831             
24832             
24833             has_other_nodes = true;
24834         }
24835         if (!nodes.length && other_nodes.length) {
24836             nodes= other_nodes;
24837         }
24838         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24839             return false;
24840         }
24841         
24842         return nodes[0];
24843     },
24844     createRange: function(sel)
24845     {
24846         // this has strange effects when using with 
24847         // top toolbar - not sure if it's a great idea.
24848         //this.editor.contentWindow.focus();
24849         if (typeof sel != "undefined") {
24850             try {
24851                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24852             } catch(e) {
24853                 return this.doc.createRange();
24854             }
24855         } else {
24856             return this.doc.createRange();
24857         }
24858     },
24859     getParentElement: function()
24860     {
24861         
24862         this.assignDocWin();
24863         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24864         
24865         var range = this.createRange(sel);
24866          
24867         try {
24868             var p = range.commonAncestorContainer;
24869             while (p.nodeType == 3) { // text node
24870                 p = p.parentNode;
24871             }
24872             return p;
24873         } catch (e) {
24874             return null;
24875         }
24876     
24877     },
24878     /***
24879      *
24880      * Range intersection.. the hard stuff...
24881      *  '-1' = before
24882      *  '0' = hits..
24883      *  '1' = after.
24884      *         [ -- selected range --- ]
24885      *   [fail]                        [fail]
24886      *
24887      *    basically..
24888      *      if end is before start or  hits it. fail.
24889      *      if start is after end or hits it fail.
24890      *
24891      *   if either hits (but other is outside. - then it's not 
24892      *   
24893      *    
24894      **/
24895     
24896     
24897     // @see http://www.thismuchiknow.co.uk/?p=64.
24898     rangeIntersectsNode : function(range, node)
24899     {
24900         var nodeRange = node.ownerDocument.createRange();
24901         try {
24902             nodeRange.selectNode(node);
24903         } catch (e) {
24904             nodeRange.selectNodeContents(node);
24905         }
24906     
24907         var rangeStartRange = range.cloneRange();
24908         rangeStartRange.collapse(true);
24909     
24910         var rangeEndRange = range.cloneRange();
24911         rangeEndRange.collapse(false);
24912     
24913         var nodeStartRange = nodeRange.cloneRange();
24914         nodeStartRange.collapse(true);
24915     
24916         var nodeEndRange = nodeRange.cloneRange();
24917         nodeEndRange.collapse(false);
24918     
24919         return rangeStartRange.compareBoundaryPoints(
24920                  Range.START_TO_START, nodeEndRange) == -1 &&
24921                rangeEndRange.compareBoundaryPoints(
24922                  Range.START_TO_START, nodeStartRange) == 1;
24923         
24924          
24925     },
24926     rangeCompareNode : function(range, node)
24927     {
24928         var nodeRange = node.ownerDocument.createRange();
24929         try {
24930             nodeRange.selectNode(node);
24931         } catch (e) {
24932             nodeRange.selectNodeContents(node);
24933         }
24934         
24935         
24936         range.collapse(true);
24937     
24938         nodeRange.collapse(true);
24939      
24940         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24941         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24942          
24943         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24944         
24945         var nodeIsBefore   =  ss == 1;
24946         var nodeIsAfter    = ee == -1;
24947         
24948         if (nodeIsBefore && nodeIsAfter) {
24949             return 0; // outer
24950         }
24951         if (!nodeIsBefore && nodeIsAfter) {
24952             return 1; //right trailed.
24953         }
24954         
24955         if (nodeIsBefore && !nodeIsAfter) {
24956             return 2;  // left trailed.
24957         }
24958         // fully contined.
24959         return 3;
24960     },
24961
24962     // private? - in a new class?
24963     cleanUpPaste :  function()
24964     {
24965         // cleans up the whole document..
24966         Roo.log('cleanuppaste');
24967         
24968         this.cleanUpChildren(this.doc.body);
24969         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24970         if (clean != this.doc.body.innerHTML) {
24971             this.doc.body.innerHTML = clean;
24972         }
24973         
24974     },
24975     
24976     cleanWordChars : function(input) {// change the chars to hex code
24977         var he = Roo.HtmlEditorCore;
24978         
24979         var output = input;
24980         Roo.each(he.swapCodes, function(sw) { 
24981             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24982             
24983             output = output.replace(swapper, sw[1]);
24984         });
24985         
24986         return output;
24987     },
24988     
24989     
24990     cleanUpChildren : function (n)
24991     {
24992         if (!n.childNodes.length) {
24993             return;
24994         }
24995         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24996            this.cleanUpChild(n.childNodes[i]);
24997         }
24998     },
24999     
25000     
25001         
25002     
25003     cleanUpChild : function (node)
25004     {
25005         var ed = this;
25006         //console.log(node);
25007         if (node.nodeName == "#text") {
25008             // clean up silly Windows -- stuff?
25009             return; 
25010         }
25011         if (node.nodeName == "#comment") {
25012             node.parentNode.removeChild(node);
25013             // clean up silly Windows -- stuff?
25014             return; 
25015         }
25016         var lcname = node.tagName.toLowerCase();
25017         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25018         // whitelist of tags..
25019         
25020         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25021             // remove node.
25022             node.parentNode.removeChild(node);
25023             return;
25024             
25025         }
25026         
25027         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25028         
25029         // spans with no attributes - just remove them..
25030         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25031             remove_keep_children = true;
25032         }
25033         
25034         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25035         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25036         
25037         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25038         //    remove_keep_children = true;
25039         //}
25040         
25041         if (remove_keep_children) {
25042             this.cleanUpChildren(node);
25043             // inserts everything just before this node...
25044             while (node.childNodes.length) {
25045                 var cn = node.childNodes[0];
25046                 node.removeChild(cn);
25047                 node.parentNode.insertBefore(cn, node);
25048             }
25049             node.parentNode.removeChild(node);
25050             return;
25051         }
25052         
25053         if (!node.attributes || !node.attributes.length) {
25054             
25055           
25056             
25057             
25058             this.cleanUpChildren(node);
25059             return;
25060         }
25061         
25062         function cleanAttr(n,v)
25063         {
25064             
25065             if (v.match(/^\./) || v.match(/^\//)) {
25066                 return;
25067             }
25068             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25069                 return;
25070             }
25071             if (v.match(/^#/)) {
25072                 return;
25073             }
25074             if (v.match(/^\{/)) { // allow template editing.
25075                 return;
25076             }
25077 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25078             node.removeAttribute(n);
25079             
25080         }
25081         
25082         var cwhite = this.cwhite;
25083         var cblack = this.cblack;
25084             
25085         function cleanStyle(n,v)
25086         {
25087             if (v.match(/expression/)) { //XSS?? should we even bother..
25088                 node.removeAttribute(n);
25089                 return;
25090             }
25091             
25092             var parts = v.split(/;/);
25093             var clean = [];
25094             
25095             Roo.each(parts, function(p) {
25096                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25097                 if (!p.length) {
25098                     return true;
25099                 }
25100                 var l = p.split(':').shift().replace(/\s+/g,'');
25101                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25102                 
25103                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25104 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25105                     //node.removeAttribute(n);
25106                     return true;
25107                 }
25108                 //Roo.log()
25109                 // only allow 'c whitelisted system attributes'
25110                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25111 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25112                     //node.removeAttribute(n);
25113                     return true;
25114                 }
25115                 
25116                 
25117                  
25118                 
25119                 clean.push(p);
25120                 return true;
25121             });
25122             if (clean.length) { 
25123                 node.setAttribute(n, clean.join(';'));
25124             } else {
25125                 node.removeAttribute(n);
25126             }
25127             
25128         }
25129         
25130         
25131         for (var i = node.attributes.length-1; i > -1 ; i--) {
25132             var a = node.attributes[i];
25133             //console.log(a);
25134             
25135             if (a.name.toLowerCase().substr(0,2)=='on')  {
25136                 node.removeAttribute(a.name);
25137                 continue;
25138             }
25139             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25140                 node.removeAttribute(a.name);
25141                 continue;
25142             }
25143             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25144                 cleanAttr(a.name,a.value); // fixme..
25145                 continue;
25146             }
25147             if (a.name == 'style') {
25148                 cleanStyle(a.name,a.value);
25149                 continue;
25150             }
25151             /// clean up MS crap..
25152             // tecnically this should be a list of valid class'es..
25153             
25154             
25155             if (a.name == 'class') {
25156                 if (a.value.match(/^Mso/)) {
25157                     node.removeAttribute('class');
25158                 }
25159                 
25160                 if (a.value.match(/^body$/)) {
25161                     node.removeAttribute('class');
25162                 }
25163                 continue;
25164             }
25165             
25166             // style cleanup!?
25167             // class cleanup?
25168             
25169         }
25170         
25171         
25172         this.cleanUpChildren(node);
25173         
25174         
25175     },
25176     
25177     /**
25178      * Clean up MS wordisms...
25179      */
25180     cleanWord : function(node)
25181     {
25182         if (!node) {
25183             this.cleanWord(this.doc.body);
25184             return;
25185         }
25186         
25187         if(
25188                 node.nodeName == 'SPAN' &&
25189                 !node.hasAttributes() &&
25190                 node.childNodes.length == 1 &&
25191                 node.firstChild.nodeName == "#text"  
25192         ) {
25193             var textNode = node.firstChild;
25194             node.removeChild(textNode);
25195             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25196                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25197             }
25198             node.parentNode.insertBefore(textNode, node);
25199             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25200                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25201             }
25202             node.parentNode.removeChild(node);
25203         }
25204         
25205         if (node.nodeName == "#text") {
25206             // clean up silly Windows -- stuff?
25207             return; 
25208         }
25209         if (node.nodeName == "#comment") {
25210             node.parentNode.removeChild(node);
25211             // clean up silly Windows -- stuff?
25212             return; 
25213         }
25214         
25215         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25216             node.parentNode.removeChild(node);
25217             return;
25218         }
25219         //Roo.log(node.tagName);
25220         // remove - but keep children..
25221         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25222             //Roo.log('-- removed');
25223             while (node.childNodes.length) {
25224                 var cn = node.childNodes[0];
25225                 node.removeChild(cn);
25226                 node.parentNode.insertBefore(cn, node);
25227                 // move node to parent - and clean it..
25228                 this.cleanWord(cn);
25229             }
25230             node.parentNode.removeChild(node);
25231             /// no need to iterate chidlren = it's got none..
25232             //this.iterateChildren(node, this.cleanWord);
25233             return;
25234         }
25235         // clean styles
25236         if (node.className.length) {
25237             
25238             var cn = node.className.split(/\W+/);
25239             var cna = [];
25240             Roo.each(cn, function(cls) {
25241                 if (cls.match(/Mso[a-zA-Z]+/)) {
25242                     return;
25243                 }
25244                 cna.push(cls);
25245             });
25246             node.className = cna.length ? cna.join(' ') : '';
25247             if (!cna.length) {
25248                 node.removeAttribute("class");
25249             }
25250         }
25251         
25252         if (node.hasAttribute("lang")) {
25253             node.removeAttribute("lang");
25254         }
25255         
25256         if (node.hasAttribute("style")) {
25257             
25258             var styles = node.getAttribute("style").split(";");
25259             var nstyle = [];
25260             Roo.each(styles, function(s) {
25261                 if (!s.match(/:/)) {
25262                     return;
25263                 }
25264                 var kv = s.split(":");
25265                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25266                     return;
25267                 }
25268                 // what ever is left... we allow.
25269                 nstyle.push(s);
25270             });
25271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25272             if (!nstyle.length) {
25273                 node.removeAttribute('style');
25274             }
25275         }
25276         this.iterateChildren(node, this.cleanWord);
25277         
25278         
25279         
25280     },
25281     /**
25282      * iterateChildren of a Node, calling fn each time, using this as the scole..
25283      * @param {DomNode} node node to iterate children of.
25284      * @param {Function} fn method of this class to call on each item.
25285      */
25286     iterateChildren : function(node, fn)
25287     {
25288         if (!node.childNodes.length) {
25289                 return;
25290         }
25291         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25292            fn.call(this, node.childNodes[i])
25293         }
25294     },
25295     
25296     
25297     /**
25298      * cleanTableWidths.
25299      *
25300      * Quite often pasting from word etc.. results in tables with column and widths.
25301      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25302      *
25303      */
25304     cleanTableWidths : function(node)
25305     {
25306          
25307          
25308         if (!node) {
25309             this.cleanTableWidths(this.doc.body);
25310             return;
25311         }
25312         
25313         // ignore list...
25314         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25315             return; 
25316         }
25317         Roo.log(node.tagName);
25318         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25319             this.iterateChildren(node, this.cleanTableWidths);
25320             return;
25321         }
25322         if (node.hasAttribute('width')) {
25323             node.removeAttribute('width');
25324         }
25325         
25326          
25327         if (node.hasAttribute("style")) {
25328             // pretty basic...
25329             
25330             var styles = node.getAttribute("style").split(";");
25331             var nstyle = [];
25332             Roo.each(styles, function(s) {
25333                 if (!s.match(/:/)) {
25334                     return;
25335                 }
25336                 var kv = s.split(":");
25337                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25338                     return;
25339                 }
25340                 // what ever is left... we allow.
25341                 nstyle.push(s);
25342             });
25343             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25344             if (!nstyle.length) {
25345                 node.removeAttribute('style');
25346             }
25347         }
25348         
25349         this.iterateChildren(node, this.cleanTableWidths);
25350         
25351         
25352     },
25353     
25354     
25355     
25356     
25357     domToHTML : function(currentElement, depth, nopadtext) {
25358         
25359         depth = depth || 0;
25360         nopadtext = nopadtext || false;
25361     
25362         if (!currentElement) {
25363             return this.domToHTML(this.doc.body);
25364         }
25365         
25366         //Roo.log(currentElement);
25367         var j;
25368         var allText = false;
25369         var nodeName = currentElement.nodeName;
25370         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25371         
25372         if  (nodeName == '#text') {
25373             
25374             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25375         }
25376         
25377         
25378         var ret = '';
25379         if (nodeName != 'BODY') {
25380              
25381             var i = 0;
25382             // Prints the node tagName, such as <A>, <IMG>, etc
25383             if (tagName) {
25384                 var attr = [];
25385                 for(i = 0; i < currentElement.attributes.length;i++) {
25386                     // quoting?
25387                     var aname = currentElement.attributes.item(i).name;
25388                     if (!currentElement.attributes.item(i).value.length) {
25389                         continue;
25390                     }
25391                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25392                 }
25393                 
25394                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25395             } 
25396             else {
25397                 
25398                 // eack
25399             }
25400         } else {
25401             tagName = false;
25402         }
25403         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25404             return ret;
25405         }
25406         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25407             nopadtext = true;
25408         }
25409         
25410         
25411         // Traverse the tree
25412         i = 0;
25413         var currentElementChild = currentElement.childNodes.item(i);
25414         var allText = true;
25415         var innerHTML  = '';
25416         lastnode = '';
25417         while (currentElementChild) {
25418             // Formatting code (indent the tree so it looks nice on the screen)
25419             var nopad = nopadtext;
25420             if (lastnode == 'SPAN') {
25421                 nopad  = true;
25422             }
25423             // text
25424             if  (currentElementChild.nodeName == '#text') {
25425                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25426                 toadd = nopadtext ? toadd : toadd.trim();
25427                 if (!nopad && toadd.length > 80) {
25428                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25429                 }
25430                 innerHTML  += toadd;
25431                 
25432                 i++;
25433                 currentElementChild = currentElement.childNodes.item(i);
25434                 lastNode = '';
25435                 continue;
25436             }
25437             allText = false;
25438             
25439             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25440                 
25441             // Recursively traverse the tree structure of the child node
25442             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25443             lastnode = currentElementChild.nodeName;
25444             i++;
25445             currentElementChild=currentElement.childNodes.item(i);
25446         }
25447         
25448         ret += innerHTML;
25449         
25450         if (!allText) {
25451                 // The remaining code is mostly for formatting the tree
25452             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25453         }
25454         
25455         
25456         if (tagName) {
25457             ret+= "</"+tagName+">";
25458         }
25459         return ret;
25460         
25461     },
25462         
25463     applyBlacklists : function()
25464     {
25465         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25466         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25467         
25468         this.white = [];
25469         this.black = [];
25470         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25471             if (b.indexOf(tag) > -1) {
25472                 return;
25473             }
25474             this.white.push(tag);
25475             
25476         }, this);
25477         
25478         Roo.each(w, function(tag) {
25479             if (b.indexOf(tag) > -1) {
25480                 return;
25481             }
25482             if (this.white.indexOf(tag) > -1) {
25483                 return;
25484             }
25485             this.white.push(tag);
25486             
25487         }, this);
25488         
25489         
25490         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25491             if (w.indexOf(tag) > -1) {
25492                 return;
25493             }
25494             this.black.push(tag);
25495             
25496         }, this);
25497         
25498         Roo.each(b, function(tag) {
25499             if (w.indexOf(tag) > -1) {
25500                 return;
25501             }
25502             if (this.black.indexOf(tag) > -1) {
25503                 return;
25504             }
25505             this.black.push(tag);
25506             
25507         }, this);
25508         
25509         
25510         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25511         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25512         
25513         this.cwhite = [];
25514         this.cblack = [];
25515         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25516             if (b.indexOf(tag) > -1) {
25517                 return;
25518             }
25519             this.cwhite.push(tag);
25520             
25521         }, this);
25522         
25523         Roo.each(w, function(tag) {
25524             if (b.indexOf(tag) > -1) {
25525                 return;
25526             }
25527             if (this.cwhite.indexOf(tag) > -1) {
25528                 return;
25529             }
25530             this.cwhite.push(tag);
25531             
25532         }, this);
25533         
25534         
25535         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25536             if (w.indexOf(tag) > -1) {
25537                 return;
25538             }
25539             this.cblack.push(tag);
25540             
25541         }, this);
25542         
25543         Roo.each(b, function(tag) {
25544             if (w.indexOf(tag) > -1) {
25545                 return;
25546             }
25547             if (this.cblack.indexOf(tag) > -1) {
25548                 return;
25549             }
25550             this.cblack.push(tag);
25551             
25552         }, this);
25553     },
25554     
25555     setStylesheets : function(stylesheets)
25556     {
25557         if(typeof(stylesheets) == 'string'){
25558             Roo.get(this.iframe.contentDocument.head).createChild({
25559                 tag : 'link',
25560                 rel : 'stylesheet',
25561                 type : 'text/css',
25562                 href : stylesheets
25563             });
25564             
25565             return;
25566         }
25567         var _this = this;
25568      
25569         Roo.each(stylesheets, function(s) {
25570             if(!s.length){
25571                 return;
25572             }
25573             
25574             Roo.get(_this.iframe.contentDocument.head).createChild({
25575                 tag : 'link',
25576                 rel : 'stylesheet',
25577                 type : 'text/css',
25578                 href : s
25579             });
25580         });
25581
25582         
25583     },
25584     
25585     removeStylesheets : function()
25586     {
25587         var _this = this;
25588         
25589         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25590             s.remove();
25591         });
25592     },
25593     
25594     setStyle : function(style)
25595     {
25596         Roo.get(this.iframe.contentDocument.head).createChild({
25597             tag : 'style',
25598             type : 'text/css',
25599             html : style
25600         });
25601
25602         return;
25603     }
25604     
25605     // hide stuff that is not compatible
25606     /**
25607      * @event blur
25608      * @hide
25609      */
25610     /**
25611      * @event change
25612      * @hide
25613      */
25614     /**
25615      * @event focus
25616      * @hide
25617      */
25618     /**
25619      * @event specialkey
25620      * @hide
25621      */
25622     /**
25623      * @cfg {String} fieldClass @hide
25624      */
25625     /**
25626      * @cfg {String} focusClass @hide
25627      */
25628     /**
25629      * @cfg {String} autoCreate @hide
25630      */
25631     /**
25632      * @cfg {String} inputType @hide
25633      */
25634     /**
25635      * @cfg {String} invalidClass @hide
25636      */
25637     /**
25638      * @cfg {String} invalidText @hide
25639      */
25640     /**
25641      * @cfg {String} msgFx @hide
25642      */
25643     /**
25644      * @cfg {String} validateOnBlur @hide
25645      */
25646 });
25647
25648 Roo.HtmlEditorCore.white = [
25649         'area', 'br', 'img', 'input', 'hr', 'wbr',
25650         
25651        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25652        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25653        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25654        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25655        'table',   'ul',         'xmp', 
25656        
25657        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25658       'thead',   'tr', 
25659      
25660       'dir', 'menu', 'ol', 'ul', 'dl',
25661        
25662       'embed',  'object'
25663 ];
25664
25665
25666 Roo.HtmlEditorCore.black = [
25667     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25668         'applet', // 
25669         'base',   'basefont', 'bgsound', 'blink',  'body', 
25670         'frame',  'frameset', 'head',    'html',   'ilayer', 
25671         'iframe', 'layer',  'link',     'meta',    'object',   
25672         'script', 'style' ,'title',  'xml' // clean later..
25673 ];
25674 Roo.HtmlEditorCore.clean = [
25675     'script', 'style', 'title', 'xml'
25676 ];
25677 Roo.HtmlEditorCore.remove = [
25678     'font'
25679 ];
25680 // attributes..
25681
25682 Roo.HtmlEditorCore.ablack = [
25683     'on'
25684 ];
25685     
25686 Roo.HtmlEditorCore.aclean = [ 
25687     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25688 ];
25689
25690 // protocols..
25691 Roo.HtmlEditorCore.pwhite= [
25692         'http',  'https',  'mailto'
25693 ];
25694
25695 // white listed style attributes.
25696 Roo.HtmlEditorCore.cwhite= [
25697       //  'text-align', /// default is to allow most things..
25698       
25699          
25700 //        'font-size'//??
25701 ];
25702
25703 // black listed style attributes.
25704 Roo.HtmlEditorCore.cblack= [
25705       //  'font-size' -- this can be set by the project 
25706 ];
25707
25708
25709 Roo.HtmlEditorCore.swapCodes   =[ 
25710     [    8211, "--" ], 
25711     [    8212, "--" ], 
25712     [    8216,  "'" ],  
25713     [    8217, "'" ],  
25714     [    8220, '"' ],  
25715     [    8221, '"' ],  
25716     [    8226, "*" ],  
25717     [    8230, "..." ]
25718 ]; 
25719
25720     /*
25721  * - LGPL
25722  *
25723  * HtmlEditor
25724  * 
25725  */
25726
25727 /**
25728  * @class Roo.bootstrap.HtmlEditor
25729  * @extends Roo.bootstrap.TextArea
25730  * Bootstrap HtmlEditor class
25731
25732  * @constructor
25733  * Create a new HtmlEditor
25734  * @param {Object} config The config object
25735  */
25736
25737 Roo.bootstrap.HtmlEditor = function(config){
25738     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25739     if (!this.toolbars) {
25740         this.toolbars = [];
25741     }
25742     
25743     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25744     this.addEvents({
25745             /**
25746              * @event initialize
25747              * Fires when the editor is fully initialized (including the iframe)
25748              * @param {HtmlEditor} this
25749              */
25750             initialize: true,
25751             /**
25752              * @event activate
25753              * Fires when the editor is first receives the focus. Any insertion must wait
25754              * until after this event.
25755              * @param {HtmlEditor} this
25756              */
25757             activate: true,
25758              /**
25759              * @event beforesync
25760              * Fires before the textarea is updated with content from the editor iframe. Return false
25761              * to cancel the sync.
25762              * @param {HtmlEditor} this
25763              * @param {String} html
25764              */
25765             beforesync: true,
25766              /**
25767              * @event beforepush
25768              * Fires before the iframe editor is updated with content from the textarea. Return false
25769              * to cancel the push.
25770              * @param {HtmlEditor} this
25771              * @param {String} html
25772              */
25773             beforepush: true,
25774              /**
25775              * @event sync
25776              * Fires when the textarea is updated with content from the editor iframe.
25777              * @param {HtmlEditor} this
25778              * @param {String} html
25779              */
25780             sync: true,
25781              /**
25782              * @event push
25783              * Fires when the iframe editor is updated with content from the textarea.
25784              * @param {HtmlEditor} this
25785              * @param {String} html
25786              */
25787             push: true,
25788              /**
25789              * @event editmodechange
25790              * Fires when the editor switches edit modes
25791              * @param {HtmlEditor} this
25792              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25793              */
25794             editmodechange: true,
25795             /**
25796              * @event editorevent
25797              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25798              * @param {HtmlEditor} this
25799              */
25800             editorevent: true,
25801             /**
25802              * @event firstfocus
25803              * Fires when on first focus - needed by toolbars..
25804              * @param {HtmlEditor} this
25805              */
25806             firstfocus: true,
25807             /**
25808              * @event autosave
25809              * Auto save the htmlEditor value as a file into Events
25810              * @param {HtmlEditor} this
25811              */
25812             autosave: true,
25813             /**
25814              * @event savedpreview
25815              * preview the saved version of htmlEditor
25816              * @param {HtmlEditor} this
25817              */
25818             savedpreview: true
25819         });
25820 };
25821
25822
25823 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25824     
25825     
25826       /**
25827      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25828      */
25829     toolbars : false,
25830     
25831      /**
25832     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25833     */
25834     btns : [],
25835    
25836      /**
25837      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25838      *                        Roo.resizable.
25839      */
25840     resizable : false,
25841      /**
25842      * @cfg {Number} height (in pixels)
25843      */   
25844     height: 300,
25845    /**
25846      * @cfg {Number} width (in pixels)
25847      */   
25848     width: false,
25849     
25850     /**
25851      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25852      * 
25853      */
25854     stylesheets: false,
25855     
25856     // id of frame..
25857     frameId: false,
25858     
25859     // private properties
25860     validationEvent : false,
25861     deferHeight: true,
25862     initialized : false,
25863     activated : false,
25864     
25865     onFocus : Roo.emptyFn,
25866     iframePad:3,
25867     hideMode:'offsets',
25868     
25869     tbContainer : false,
25870     
25871     bodyCls : '',
25872     
25873     toolbarContainer :function() {
25874         return this.wrap.select('.x-html-editor-tb',true).first();
25875     },
25876
25877     /**
25878      * Protected method that will not generally be called directly. It
25879      * is called when the editor creates its toolbar. Override this method if you need to
25880      * add custom toolbar buttons.
25881      * @param {HtmlEditor} editor
25882      */
25883     createToolbar : function(){
25884         Roo.log('renewing');
25885         Roo.log("create toolbars");
25886         
25887         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25888         this.toolbars[0].render(this.toolbarContainer());
25889         
25890         return;
25891         
25892 //        if (!editor.toolbars || !editor.toolbars.length) {
25893 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25894 //        }
25895 //        
25896 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25897 //            editor.toolbars[i] = Roo.factory(
25898 //                    typeof(editor.toolbars[i]) == 'string' ?
25899 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25900 //                Roo.bootstrap.HtmlEditor);
25901 //            editor.toolbars[i].init(editor);
25902 //        }
25903     },
25904
25905      
25906     // private
25907     onRender : function(ct, position)
25908     {
25909        // Roo.log("Call onRender: " + this.xtype);
25910         var _t = this;
25911         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25912       
25913         this.wrap = this.inputEl().wrap({
25914             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25915         });
25916         
25917         this.editorcore.onRender(ct, position);
25918          
25919         if (this.resizable) {
25920             this.resizeEl = new Roo.Resizable(this.wrap, {
25921                 pinned : true,
25922                 wrap: true,
25923                 dynamic : true,
25924                 minHeight : this.height,
25925                 height: this.height,
25926                 handles : this.resizable,
25927                 width: this.width,
25928                 listeners : {
25929                     resize : function(r, w, h) {
25930                         _t.onResize(w,h); // -something
25931                     }
25932                 }
25933             });
25934             
25935         }
25936         this.createToolbar(this);
25937        
25938         
25939         if(!this.width && this.resizable){
25940             this.setSize(this.wrap.getSize());
25941         }
25942         if (this.resizeEl) {
25943             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25944             // should trigger onReize..
25945         }
25946         
25947     },
25948
25949     // private
25950     onResize : function(w, h)
25951     {
25952         Roo.log('resize: ' +w + ',' + h );
25953         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25954         var ew = false;
25955         var eh = false;
25956         
25957         if(this.inputEl() ){
25958             if(typeof w == 'number'){
25959                 var aw = w - this.wrap.getFrameWidth('lr');
25960                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25961                 ew = aw;
25962             }
25963             if(typeof h == 'number'){
25964                  var tbh = -11;  // fixme it needs to tool bar size!
25965                 for (var i =0; i < this.toolbars.length;i++) {
25966                     // fixme - ask toolbars for heights?
25967                     tbh += this.toolbars[i].el.getHeight();
25968                     //if (this.toolbars[i].footer) {
25969                     //    tbh += this.toolbars[i].footer.el.getHeight();
25970                     //}
25971                 }
25972               
25973                 
25974                 
25975                 
25976                 
25977                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25978                 ah -= 5; // knock a few pixes off for look..
25979                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25980                 var eh = ah;
25981             }
25982         }
25983         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25984         this.editorcore.onResize(ew,eh);
25985         
25986     },
25987
25988     /**
25989      * Toggles the editor between standard and source edit mode.
25990      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25991      */
25992     toggleSourceEdit : function(sourceEditMode)
25993     {
25994         this.editorcore.toggleSourceEdit(sourceEditMode);
25995         
25996         if(this.editorcore.sourceEditMode){
25997             Roo.log('editor - showing textarea');
25998             
25999 //            Roo.log('in');
26000 //            Roo.log(this.syncValue());
26001             this.syncValue();
26002             this.inputEl().removeClass(['hide', 'x-hidden']);
26003             this.inputEl().dom.removeAttribute('tabIndex');
26004             this.inputEl().focus();
26005         }else{
26006             Roo.log('editor - hiding textarea');
26007 //            Roo.log('out')
26008 //            Roo.log(this.pushValue()); 
26009             this.pushValue();
26010             
26011             this.inputEl().addClass(['hide', 'x-hidden']);
26012             this.inputEl().dom.setAttribute('tabIndex', -1);
26013             //this.deferFocus();
26014         }
26015          
26016         if(this.resizable){
26017             this.setSize(this.wrap.getSize());
26018         }
26019         
26020         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26021     },
26022  
26023     // private (for BoxComponent)
26024     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26025
26026     // private (for BoxComponent)
26027     getResizeEl : function(){
26028         return this.wrap;
26029     },
26030
26031     // private (for BoxComponent)
26032     getPositionEl : function(){
26033         return this.wrap;
26034     },
26035
26036     // private
26037     initEvents : function(){
26038         this.originalValue = this.getValue();
26039     },
26040
26041 //    /**
26042 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26043 //     * @method
26044 //     */
26045 //    markInvalid : Roo.emptyFn,
26046 //    /**
26047 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26048 //     * @method
26049 //     */
26050 //    clearInvalid : Roo.emptyFn,
26051
26052     setValue : function(v){
26053         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26054         this.editorcore.pushValue();
26055     },
26056
26057      
26058     // private
26059     deferFocus : function(){
26060         this.focus.defer(10, this);
26061     },
26062
26063     // doc'ed in Field
26064     focus : function(){
26065         this.editorcore.focus();
26066         
26067     },
26068       
26069
26070     // private
26071     onDestroy : function(){
26072         
26073         
26074         
26075         if(this.rendered){
26076             
26077             for (var i =0; i < this.toolbars.length;i++) {
26078                 // fixme - ask toolbars for heights?
26079                 this.toolbars[i].onDestroy();
26080             }
26081             
26082             this.wrap.dom.innerHTML = '';
26083             this.wrap.remove();
26084         }
26085     },
26086
26087     // private
26088     onFirstFocus : function(){
26089         //Roo.log("onFirstFocus");
26090         this.editorcore.onFirstFocus();
26091          for (var i =0; i < this.toolbars.length;i++) {
26092             this.toolbars[i].onFirstFocus();
26093         }
26094         
26095     },
26096     
26097     // private
26098     syncValue : function()
26099     {   
26100         this.editorcore.syncValue();
26101     },
26102     
26103     pushValue : function()
26104     {   
26105         this.editorcore.pushValue();
26106     }
26107      
26108     
26109     // hide stuff that is not compatible
26110     /**
26111      * @event blur
26112      * @hide
26113      */
26114     /**
26115      * @event change
26116      * @hide
26117      */
26118     /**
26119      * @event focus
26120      * @hide
26121      */
26122     /**
26123      * @event specialkey
26124      * @hide
26125      */
26126     /**
26127      * @cfg {String} fieldClass @hide
26128      */
26129     /**
26130      * @cfg {String} focusClass @hide
26131      */
26132     /**
26133      * @cfg {String} autoCreate @hide
26134      */
26135     /**
26136      * @cfg {String} inputType @hide
26137      */
26138      
26139     /**
26140      * @cfg {String} invalidText @hide
26141      */
26142     /**
26143      * @cfg {String} msgFx @hide
26144      */
26145     /**
26146      * @cfg {String} validateOnBlur @hide
26147      */
26148 });
26149  
26150     
26151    
26152    
26153    
26154       
26155 Roo.namespace('Roo.bootstrap.htmleditor');
26156 /**
26157  * @class Roo.bootstrap.HtmlEditorToolbar1
26158  * Basic Toolbar
26159  * 
26160  * @example
26161  * Usage:
26162  *
26163  new Roo.bootstrap.HtmlEditor({
26164     ....
26165     toolbars : [
26166         new Roo.bootstrap.HtmlEditorToolbar1({
26167             disable : { fonts: 1 , format: 1, ..., ... , ...],
26168             btns : [ .... ]
26169         })
26170     }
26171      
26172  * 
26173  * @cfg {Object} disable List of elements to disable..
26174  * @cfg {Array} btns List of additional buttons.
26175  * 
26176  * 
26177  * NEEDS Extra CSS? 
26178  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26179  */
26180  
26181 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26182 {
26183     
26184     Roo.apply(this, config);
26185     
26186     // default disabled, based on 'good practice'..
26187     this.disable = this.disable || {};
26188     Roo.applyIf(this.disable, {
26189         fontSize : true,
26190         colors : true,
26191         specialElements : true
26192     });
26193     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26194     
26195     this.editor = config.editor;
26196     this.editorcore = config.editor.editorcore;
26197     
26198     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26199     
26200     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26201     // dont call parent... till later.
26202 }
26203 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26204      
26205     bar : true,
26206     
26207     editor : false,
26208     editorcore : false,
26209     
26210     
26211     formats : [
26212         "p" ,  
26213         "h1","h2","h3","h4","h5","h6", 
26214         "pre", "code", 
26215         "abbr", "acronym", "address", "cite", "samp", "var",
26216         'div','span'
26217     ],
26218     
26219     onRender : function(ct, position)
26220     {
26221        // Roo.log("Call onRender: " + this.xtype);
26222         
26223        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26224        Roo.log(this.el);
26225        this.el.dom.style.marginBottom = '0';
26226        var _this = this;
26227        var editorcore = this.editorcore;
26228        var editor= this.editor;
26229        
26230        var children = [];
26231        var btn = function(id,cmd , toggle, handler, html){
26232        
26233             var  event = toggle ? 'toggle' : 'click';
26234        
26235             var a = {
26236                 size : 'sm',
26237                 xtype: 'Button',
26238                 xns: Roo.bootstrap,
26239                 //glyphicon : id,
26240                 fa: id,
26241                 cmd : id || cmd,
26242                 enableToggle:toggle !== false,
26243                 html : html || '',
26244                 pressed : toggle ? false : null,
26245                 listeners : {}
26246             };
26247             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26248                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26249             };
26250             children.push(a);
26251             return a;
26252        }
26253        
26254     //    var cb_box = function...
26255         
26256         var style = {
26257                 xtype: 'Button',
26258                 size : 'sm',
26259                 xns: Roo.bootstrap,
26260                 fa : 'font',
26261                 //html : 'submit'
26262                 menu : {
26263                     xtype: 'Menu',
26264                     xns: Roo.bootstrap,
26265                     items:  []
26266                 }
26267         };
26268         Roo.each(this.formats, function(f) {
26269             style.menu.items.push({
26270                 xtype :'MenuItem',
26271                 xns: Roo.bootstrap,
26272                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26273                 tagname : f,
26274                 listeners : {
26275                     click : function()
26276                     {
26277                         editorcore.insertTag(this.tagname);
26278                         editor.focus();
26279                     }
26280                 }
26281                 
26282             });
26283         });
26284         children.push(style);   
26285         
26286         btn('bold',false,true);
26287         btn('italic',false,true);
26288         btn('align-left', 'justifyleft',true);
26289         btn('align-center', 'justifycenter',true);
26290         btn('align-right' , 'justifyright',true);
26291         btn('link', false, false, function(btn) {
26292             //Roo.log("create link?");
26293             var url = prompt(this.createLinkText, this.defaultLinkValue);
26294             if(url && url != 'http:/'+'/'){
26295                 this.editorcore.relayCmd('createlink', url);
26296             }
26297         }),
26298         btn('list','insertunorderedlist',true);
26299         btn('pencil', false,true, function(btn){
26300                 Roo.log(this);
26301                 this.toggleSourceEdit(btn.pressed);
26302         });
26303         
26304         if (this.editor.btns.length > 0) {
26305             for (var i = 0; i<this.editor.btns.length; i++) {
26306                 children.push(this.editor.btns[i]);
26307             }
26308         }
26309         
26310         /*
26311         var cog = {
26312                 xtype: 'Button',
26313                 size : 'sm',
26314                 xns: Roo.bootstrap,
26315                 glyphicon : 'cog',
26316                 //html : 'submit'
26317                 menu : {
26318                     xtype: 'Menu',
26319                     xns: Roo.bootstrap,
26320                     items:  []
26321                 }
26322         };
26323         
26324         cog.menu.items.push({
26325             xtype :'MenuItem',
26326             xns: Roo.bootstrap,
26327             html : Clean styles,
26328             tagname : f,
26329             listeners : {
26330                 click : function()
26331                 {
26332                     editorcore.insertTag(this.tagname);
26333                     editor.focus();
26334                 }
26335             }
26336             
26337         });
26338        */
26339         
26340          
26341        this.xtype = 'NavSimplebar';
26342         
26343         for(var i=0;i< children.length;i++) {
26344             
26345             this.buttons.add(this.addxtypeChild(children[i]));
26346             
26347         }
26348         
26349         editor.on('editorevent', this.updateToolbar, this);
26350     },
26351     onBtnClick : function(id)
26352     {
26353        this.editorcore.relayCmd(id);
26354        this.editorcore.focus();
26355     },
26356     
26357     /**
26358      * Protected method that will not generally be called directly. It triggers
26359      * a toolbar update by reading the markup state of the current selection in the editor.
26360      */
26361     updateToolbar: function(){
26362
26363         if(!this.editorcore.activated){
26364             this.editor.onFirstFocus(); // is this neeed?
26365             return;
26366         }
26367
26368         var btns = this.buttons; 
26369         var doc = this.editorcore.doc;
26370         btns.get('bold').setActive(doc.queryCommandState('bold'));
26371         btns.get('italic').setActive(doc.queryCommandState('italic'));
26372         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26373         
26374         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26375         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26376         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26377         
26378         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26379         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26380          /*
26381         
26382         var ans = this.editorcore.getAllAncestors();
26383         if (this.formatCombo) {
26384             
26385             
26386             var store = this.formatCombo.store;
26387             this.formatCombo.setValue("");
26388             for (var i =0; i < ans.length;i++) {
26389                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26390                     // select it..
26391                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26392                     break;
26393                 }
26394             }
26395         }
26396         
26397         
26398         
26399         // hides menus... - so this cant be on a menu...
26400         Roo.bootstrap.MenuMgr.hideAll();
26401         */
26402         Roo.bootstrap.MenuMgr.hideAll();
26403         //this.editorsyncValue();
26404     },
26405     onFirstFocus: function() {
26406         this.buttons.each(function(item){
26407            item.enable();
26408         });
26409     },
26410     toggleSourceEdit : function(sourceEditMode){
26411         
26412           
26413         if(sourceEditMode){
26414             Roo.log("disabling buttons");
26415            this.buttons.each( function(item){
26416                 if(item.cmd != 'pencil'){
26417                     item.disable();
26418                 }
26419             });
26420           
26421         }else{
26422             Roo.log("enabling buttons");
26423             if(this.editorcore.initialized){
26424                 this.buttons.each( function(item){
26425                     item.enable();
26426                 });
26427             }
26428             
26429         }
26430         Roo.log("calling toggole on editor");
26431         // tell the editor that it's been pressed..
26432         this.editor.toggleSourceEdit(sourceEditMode);
26433        
26434     }
26435 });
26436
26437
26438
26439
26440  
26441 /*
26442  * - LGPL
26443  */
26444
26445 /**
26446  * @class Roo.bootstrap.Markdown
26447  * @extends Roo.bootstrap.TextArea
26448  * Bootstrap Showdown editable area
26449  * @cfg {string} content
26450  * 
26451  * @constructor
26452  * Create a new Showdown
26453  */
26454
26455 Roo.bootstrap.Markdown = function(config){
26456     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26457    
26458 };
26459
26460 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26461     
26462     editing :false,
26463     
26464     initEvents : function()
26465     {
26466         
26467         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26468         this.markdownEl = this.el.createChild({
26469             cls : 'roo-markdown-area'
26470         });
26471         this.inputEl().addClass('d-none');
26472         if (this.getValue() == '') {
26473             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26474             
26475         } else {
26476             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26477         }
26478         this.markdownEl.on('click', this.toggleTextEdit, this);
26479         this.on('blur', this.toggleTextEdit, this);
26480         this.on('specialkey', this.resizeTextArea, this);
26481     },
26482     
26483     toggleTextEdit : function()
26484     {
26485         var sh = this.markdownEl.getHeight();
26486         this.inputEl().addClass('d-none');
26487         this.markdownEl.addClass('d-none');
26488         if (!this.editing) {
26489             // show editor?
26490             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26491             this.inputEl().removeClass('d-none');
26492             this.inputEl().focus();
26493             this.editing = true;
26494             return;
26495         }
26496         // show showdown...
26497         this.updateMarkdown();
26498         this.markdownEl.removeClass('d-none');
26499         this.editing = false;
26500         return;
26501     },
26502     updateMarkdown : function()
26503     {
26504         if (this.getValue() == '') {
26505             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26506             return;
26507         }
26508  
26509         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26510     },
26511     
26512     resizeTextArea: function () {
26513         
26514         var sh = 100;
26515         Roo.log([sh, this.getValue().split("\n").length * 30]);
26516         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26517     },
26518     setValue : function(val)
26519     {
26520         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26521         if (!this.editing) {
26522             this.updateMarkdown();
26523         }
26524         
26525     },
26526     focus : function()
26527     {
26528         if (!this.editing) {
26529             this.toggleTextEdit();
26530         }
26531         
26532     }
26533
26534
26535 });
26536 /**
26537  * @class Roo.bootstrap.Table.AbstractSelectionModel
26538  * @extends Roo.util.Observable
26539  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26540  * implemented by descendant classes.  This class should not be directly instantiated.
26541  * @constructor
26542  */
26543 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26544     this.locked = false;
26545     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26546 };
26547
26548
26549 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26550     /** @ignore Called by the grid automatically. Do not call directly. */
26551     init : function(grid){
26552         this.grid = grid;
26553         this.initEvents();
26554     },
26555
26556     /**
26557      * Locks the selections.
26558      */
26559     lock : function(){
26560         this.locked = true;
26561     },
26562
26563     /**
26564      * Unlocks the selections.
26565      */
26566     unlock : function(){
26567         this.locked = false;
26568     },
26569
26570     /**
26571      * Returns true if the selections are locked.
26572      * @return {Boolean}
26573      */
26574     isLocked : function(){
26575         return this.locked;
26576     },
26577     
26578     
26579     initEvents : function ()
26580     {
26581         
26582     }
26583 });
26584 /**
26585  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26586  * @class Roo.bootstrap.Table.RowSelectionModel
26587  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26588  * It supports multiple selections and keyboard selection/navigation. 
26589  * @constructor
26590  * @param {Object} config
26591  */
26592
26593 Roo.bootstrap.Table.RowSelectionModel = function(config){
26594     Roo.apply(this, config);
26595     this.selections = new Roo.util.MixedCollection(false, function(o){
26596         return o.id;
26597     });
26598
26599     this.last = false;
26600     this.lastActive = false;
26601
26602     this.addEvents({
26603         /**
26604              * @event selectionchange
26605              * Fires when the selection changes
26606              * @param {SelectionModel} this
26607              */
26608             "selectionchange" : true,
26609         /**
26610              * @event afterselectionchange
26611              * Fires after the selection changes (eg. by key press or clicking)
26612              * @param {SelectionModel} this
26613              */
26614             "afterselectionchange" : true,
26615         /**
26616              * @event beforerowselect
26617              * Fires when a row is selected being selected, return false to cancel.
26618              * @param {SelectionModel} this
26619              * @param {Number} rowIndex The selected index
26620              * @param {Boolean} keepExisting False if other selections will be cleared
26621              */
26622             "beforerowselect" : true,
26623         /**
26624              * @event rowselect
26625              * Fires when a row is selected.
26626              * @param {SelectionModel} this
26627              * @param {Number} rowIndex The selected index
26628              * @param {Roo.data.Record} r The record
26629              */
26630             "rowselect" : true,
26631         /**
26632              * @event rowdeselect
26633              * Fires when a row is deselected.
26634              * @param {SelectionModel} this
26635              * @param {Number} rowIndex The selected index
26636              */
26637         "rowdeselect" : true
26638     });
26639     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26640     this.locked = false;
26641  };
26642
26643 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26644     /**
26645      * @cfg {Boolean} singleSelect
26646      * True to allow selection of only one row at a time (defaults to false)
26647      */
26648     singleSelect : false,
26649
26650     // private
26651     initEvents : function()
26652     {
26653
26654         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26655         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26656         //}else{ // allow click to work like normal
26657          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26658         //}
26659         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26660         this.grid.on("rowclick", this.handleMouseDown, this);
26661         
26662         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26663             "up" : function(e){
26664                 if(!e.shiftKey){
26665                     this.selectPrevious(e.shiftKey);
26666                 }else if(this.last !== false && this.lastActive !== false){
26667                     var last = this.last;
26668                     this.selectRange(this.last,  this.lastActive-1);
26669                     this.grid.getView().focusRow(this.lastActive);
26670                     if(last !== false){
26671                         this.last = last;
26672                     }
26673                 }else{
26674                     this.selectFirstRow();
26675                 }
26676                 this.fireEvent("afterselectionchange", this);
26677             },
26678             "down" : function(e){
26679                 if(!e.shiftKey){
26680                     this.selectNext(e.shiftKey);
26681                 }else if(this.last !== false && this.lastActive !== false){
26682                     var last = this.last;
26683                     this.selectRange(this.last,  this.lastActive+1);
26684                     this.grid.getView().focusRow(this.lastActive);
26685                     if(last !== false){
26686                         this.last = last;
26687                     }
26688                 }else{
26689                     this.selectFirstRow();
26690                 }
26691                 this.fireEvent("afterselectionchange", this);
26692             },
26693             scope: this
26694         });
26695         this.grid.store.on('load', function(){
26696             this.selections.clear();
26697         },this);
26698         /*
26699         var view = this.grid.view;
26700         view.on("refresh", this.onRefresh, this);
26701         view.on("rowupdated", this.onRowUpdated, this);
26702         view.on("rowremoved", this.onRemove, this);
26703         */
26704     },
26705
26706     // private
26707     onRefresh : function()
26708     {
26709         var ds = this.grid.store, i, v = this.grid.view;
26710         var s = this.selections;
26711         s.each(function(r){
26712             if((i = ds.indexOfId(r.id)) != -1){
26713                 v.onRowSelect(i);
26714             }else{
26715                 s.remove(r);
26716             }
26717         });
26718     },
26719
26720     // private
26721     onRemove : function(v, index, r){
26722         this.selections.remove(r);
26723     },
26724
26725     // private
26726     onRowUpdated : function(v, index, r){
26727         if(this.isSelected(r)){
26728             v.onRowSelect(index);
26729         }
26730     },
26731
26732     /**
26733      * Select records.
26734      * @param {Array} records The records to select
26735      * @param {Boolean} keepExisting (optional) True to keep existing selections
26736      */
26737     selectRecords : function(records, keepExisting)
26738     {
26739         if(!keepExisting){
26740             this.clearSelections();
26741         }
26742             var ds = this.grid.store;
26743         for(var i = 0, len = records.length; i < len; i++){
26744             this.selectRow(ds.indexOf(records[i]), true);
26745         }
26746     },
26747
26748     /**
26749      * Gets the number of selected rows.
26750      * @return {Number}
26751      */
26752     getCount : function(){
26753         return this.selections.length;
26754     },
26755
26756     /**
26757      * Selects the first row in the grid.
26758      */
26759     selectFirstRow : function(){
26760         this.selectRow(0);
26761     },
26762
26763     /**
26764      * Select the last row.
26765      * @param {Boolean} keepExisting (optional) True to keep existing selections
26766      */
26767     selectLastRow : function(keepExisting){
26768         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26769         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26770     },
26771
26772     /**
26773      * Selects the row immediately following the last selected row.
26774      * @param {Boolean} keepExisting (optional) True to keep existing selections
26775      */
26776     selectNext : function(keepExisting)
26777     {
26778             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26779             this.selectRow(this.last+1, keepExisting);
26780             this.grid.getView().focusRow(this.last);
26781         }
26782     },
26783
26784     /**
26785      * Selects the row that precedes the last selected row.
26786      * @param {Boolean} keepExisting (optional) True to keep existing selections
26787      */
26788     selectPrevious : function(keepExisting){
26789         if(this.last){
26790             this.selectRow(this.last-1, keepExisting);
26791             this.grid.getView().focusRow(this.last);
26792         }
26793     },
26794
26795     /**
26796      * Returns the selected records
26797      * @return {Array} Array of selected records
26798      */
26799     getSelections : function(){
26800         return [].concat(this.selections.items);
26801     },
26802
26803     /**
26804      * Returns the first selected record.
26805      * @return {Record}
26806      */
26807     getSelected : function(){
26808         return this.selections.itemAt(0);
26809     },
26810
26811
26812     /**
26813      * Clears all selections.
26814      */
26815     clearSelections : function(fast)
26816     {
26817         if(this.locked) {
26818             return;
26819         }
26820         if(fast !== true){
26821                 var ds = this.grid.store;
26822             var s = this.selections;
26823             s.each(function(r){
26824                 this.deselectRow(ds.indexOfId(r.id));
26825             }, this);
26826             s.clear();
26827         }else{
26828             this.selections.clear();
26829         }
26830         this.last = false;
26831     },
26832
26833
26834     /**
26835      * Selects all rows.
26836      */
26837     selectAll : function(){
26838         if(this.locked) {
26839             return;
26840         }
26841         this.selections.clear();
26842         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26843             this.selectRow(i, true);
26844         }
26845     },
26846
26847     /**
26848      * Returns True if there is a selection.
26849      * @return {Boolean}
26850      */
26851     hasSelection : function(){
26852         return this.selections.length > 0;
26853     },
26854
26855     /**
26856      * Returns True if the specified row is selected.
26857      * @param {Number/Record} record The record or index of the record to check
26858      * @return {Boolean}
26859      */
26860     isSelected : function(index){
26861             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26862         return (r && this.selections.key(r.id) ? true : false);
26863     },
26864
26865     /**
26866      * Returns True if the specified record id is selected.
26867      * @param {String} id The id of record to check
26868      * @return {Boolean}
26869      */
26870     isIdSelected : function(id){
26871         return (this.selections.key(id) ? true : false);
26872     },
26873
26874
26875     // private
26876     handleMouseDBClick : function(e, t){
26877         
26878     },
26879     // private
26880     handleMouseDown : function(e, t)
26881     {
26882             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26883         if(this.isLocked() || rowIndex < 0 ){
26884             return;
26885         };
26886         if(e.shiftKey && this.last !== false){
26887             var last = this.last;
26888             this.selectRange(last, rowIndex, e.ctrlKey);
26889             this.last = last; // reset the last
26890             t.focus();
26891     
26892         }else{
26893             var isSelected = this.isSelected(rowIndex);
26894             //Roo.log("select row:" + rowIndex);
26895             if(isSelected){
26896                 this.deselectRow(rowIndex);
26897             } else {
26898                         this.selectRow(rowIndex, true);
26899             }
26900     
26901             /*
26902                 if(e.button !== 0 && isSelected){
26903                 alert('rowIndex 2: ' + rowIndex);
26904                     view.focusRow(rowIndex);
26905                 }else if(e.ctrlKey && isSelected){
26906                     this.deselectRow(rowIndex);
26907                 }else if(!isSelected){
26908                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26909                     view.focusRow(rowIndex);
26910                 }
26911             */
26912         }
26913         this.fireEvent("afterselectionchange", this);
26914     },
26915     // private
26916     handleDragableRowClick :  function(grid, rowIndex, e) 
26917     {
26918         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26919             this.selectRow(rowIndex, false);
26920             grid.view.focusRow(rowIndex);
26921              this.fireEvent("afterselectionchange", this);
26922         }
26923     },
26924     
26925     /**
26926      * Selects multiple rows.
26927      * @param {Array} rows Array of the indexes of the row to select
26928      * @param {Boolean} keepExisting (optional) True to keep existing selections
26929      */
26930     selectRows : function(rows, keepExisting){
26931         if(!keepExisting){
26932             this.clearSelections();
26933         }
26934         for(var i = 0, len = rows.length; i < len; i++){
26935             this.selectRow(rows[i], true);
26936         }
26937     },
26938
26939     /**
26940      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26941      * @param {Number} startRow The index of the first row in the range
26942      * @param {Number} endRow The index of the last row in the range
26943      * @param {Boolean} keepExisting (optional) True to retain existing selections
26944      */
26945     selectRange : function(startRow, endRow, keepExisting){
26946         if(this.locked) {
26947             return;
26948         }
26949         if(!keepExisting){
26950             this.clearSelections();
26951         }
26952         if(startRow <= endRow){
26953             for(var i = startRow; i <= endRow; i++){
26954                 this.selectRow(i, true);
26955             }
26956         }else{
26957             for(var i = startRow; i >= endRow; i--){
26958                 this.selectRow(i, true);
26959             }
26960         }
26961     },
26962
26963     /**
26964      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26965      * @param {Number} startRow The index of the first row in the range
26966      * @param {Number} endRow The index of the last row in the range
26967      */
26968     deselectRange : function(startRow, endRow, preventViewNotify){
26969         if(this.locked) {
26970             return;
26971         }
26972         for(var i = startRow; i <= endRow; i++){
26973             this.deselectRow(i, preventViewNotify);
26974         }
26975     },
26976
26977     /**
26978      * Selects a row.
26979      * @param {Number} row The index of the row to select
26980      * @param {Boolean} keepExisting (optional) True to keep existing selections
26981      */
26982     selectRow : function(index, keepExisting, preventViewNotify)
26983     {
26984             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26985             return;
26986         }
26987         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26988             if(!keepExisting || this.singleSelect){
26989                 this.clearSelections();
26990             }
26991             
26992             var r = this.grid.store.getAt(index);
26993             //console.log('selectRow - record id :' + r.id);
26994             
26995             this.selections.add(r);
26996             this.last = this.lastActive = index;
26997             if(!preventViewNotify){
26998                 var proxy = new Roo.Element(
26999                                 this.grid.getRowDom(index)
27000                 );
27001                 proxy.addClass('bg-info info');
27002             }
27003             this.fireEvent("rowselect", this, index, r);
27004             this.fireEvent("selectionchange", this);
27005         }
27006     },
27007
27008     /**
27009      * Deselects a row.
27010      * @param {Number} row The index of the row to deselect
27011      */
27012     deselectRow : function(index, preventViewNotify)
27013     {
27014         if(this.locked) {
27015             return;
27016         }
27017         if(this.last == index){
27018             this.last = false;
27019         }
27020         if(this.lastActive == index){
27021             this.lastActive = false;
27022         }
27023         
27024         var r = this.grid.store.getAt(index);
27025         if (!r) {
27026             return;
27027         }
27028         
27029         this.selections.remove(r);
27030         //.console.log('deselectRow - record id :' + r.id);
27031         if(!preventViewNotify){
27032         
27033             var proxy = new Roo.Element(
27034                 this.grid.getRowDom(index)
27035             );
27036             proxy.removeClass('bg-info info');
27037         }
27038         this.fireEvent("rowdeselect", this, index);
27039         this.fireEvent("selectionchange", this);
27040     },
27041
27042     // private
27043     restoreLast : function(){
27044         if(this._last){
27045             this.last = this._last;
27046         }
27047     },
27048
27049     // private
27050     acceptsNav : function(row, col, cm){
27051         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27052     },
27053
27054     // private
27055     onEditorKey : function(field, e){
27056         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27057         if(k == e.TAB){
27058             e.stopEvent();
27059             ed.completeEdit();
27060             if(e.shiftKey){
27061                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27062             }else{
27063                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27064             }
27065         }else if(k == e.ENTER && !e.ctrlKey){
27066             e.stopEvent();
27067             ed.completeEdit();
27068             if(e.shiftKey){
27069                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27070             }else{
27071                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27072             }
27073         }else if(k == e.ESC){
27074             ed.cancelEdit();
27075         }
27076         if(newCell){
27077             g.startEditing(newCell[0], newCell[1]);
27078         }
27079     }
27080 });
27081 /*
27082  * Based on:
27083  * Ext JS Library 1.1.1
27084  * Copyright(c) 2006-2007, Ext JS, LLC.
27085  *
27086  * Originally Released Under LGPL - original licence link has changed is not relivant.
27087  *
27088  * Fork - LGPL
27089  * <script type="text/javascript">
27090  */
27091  
27092 /**
27093  * @class Roo.bootstrap.PagingToolbar
27094  * @extends Roo.bootstrap.NavSimplebar
27095  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27096  * @constructor
27097  * Create a new PagingToolbar
27098  * @param {Object} config The config object
27099  * @param {Roo.data.Store} store
27100  */
27101 Roo.bootstrap.PagingToolbar = function(config)
27102 {
27103     // old args format still supported... - xtype is prefered..
27104         // created from xtype...
27105     
27106     this.ds = config.dataSource;
27107     
27108     if (config.store && !this.ds) {
27109         this.store= Roo.factory(config.store, Roo.data);
27110         this.ds = this.store;
27111         this.ds.xmodule = this.xmodule || false;
27112     }
27113     
27114     this.toolbarItems = [];
27115     if (config.items) {
27116         this.toolbarItems = config.items;
27117     }
27118     
27119     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27120     
27121     this.cursor = 0;
27122     
27123     if (this.ds) { 
27124         this.bind(this.ds);
27125     }
27126     
27127     if (Roo.bootstrap.version == 4) {
27128         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27129     } else {
27130         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27131     }
27132     
27133 };
27134
27135 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27136     /**
27137      * @cfg {Roo.data.Store} dataSource
27138      * The underlying data store providing the paged data
27139      */
27140     /**
27141      * @cfg {String/HTMLElement/Element} container
27142      * container The id or element that will contain the toolbar
27143      */
27144     /**
27145      * @cfg {Boolean} displayInfo
27146      * True to display the displayMsg (defaults to false)
27147      */
27148     /**
27149      * @cfg {Number} pageSize
27150      * The number of records to display per page (defaults to 20)
27151      */
27152     pageSize: 20,
27153     /**
27154      * @cfg {String} displayMsg
27155      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27156      */
27157     displayMsg : 'Displaying {0} - {1} of {2}',
27158     /**
27159      * @cfg {String} emptyMsg
27160      * The message to display when no records are found (defaults to "No data to display")
27161      */
27162     emptyMsg : 'No data to display',
27163     /**
27164      * Customizable piece of the default paging text (defaults to "Page")
27165      * @type String
27166      */
27167     beforePageText : "Page",
27168     /**
27169      * Customizable piece of the default paging text (defaults to "of %0")
27170      * @type String
27171      */
27172     afterPageText : "of {0}",
27173     /**
27174      * Customizable piece of the default paging text (defaults to "First Page")
27175      * @type String
27176      */
27177     firstText : "First Page",
27178     /**
27179      * Customizable piece of the default paging text (defaults to "Previous Page")
27180      * @type String
27181      */
27182     prevText : "Previous Page",
27183     /**
27184      * Customizable piece of the default paging text (defaults to "Next Page")
27185      * @type String
27186      */
27187     nextText : "Next Page",
27188     /**
27189      * Customizable piece of the default paging text (defaults to "Last Page")
27190      * @type String
27191      */
27192     lastText : "Last Page",
27193     /**
27194      * Customizable piece of the default paging text (defaults to "Refresh")
27195      * @type String
27196      */
27197     refreshText : "Refresh",
27198
27199     buttons : false,
27200     // private
27201     onRender : function(ct, position) 
27202     {
27203         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27204         this.navgroup.parentId = this.id;
27205         this.navgroup.onRender(this.el, null);
27206         // add the buttons to the navgroup
27207         
27208         if(this.displayInfo){
27209             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27210             this.displayEl = this.el.select('.x-paging-info', true).first();
27211 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27212 //            this.displayEl = navel.el.select('span',true).first();
27213         }
27214         
27215         var _this = this;
27216         
27217         if(this.buttons){
27218             Roo.each(_this.buttons, function(e){ // this might need to use render????
27219                Roo.factory(e).render(_this.el);
27220             });
27221         }
27222             
27223         Roo.each(_this.toolbarItems, function(e) {
27224             _this.navgroup.addItem(e);
27225         });
27226         
27227         
27228         this.first = this.navgroup.addItem({
27229             tooltip: this.firstText,
27230             cls: "prev btn-outline-secondary",
27231             html : ' <i class="fa fa-step-backward"></i>',
27232             disabled: true,
27233             preventDefault: true,
27234             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27235         });
27236         
27237         this.prev =  this.navgroup.addItem({
27238             tooltip: this.prevText,
27239             cls: "prev btn-outline-secondary",
27240             html : ' <i class="fa fa-backward"></i>',
27241             disabled: true,
27242             preventDefault: true,
27243             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27244         });
27245     //this.addSeparator();
27246         
27247         
27248         var field = this.navgroup.addItem( {
27249             tagtype : 'span',
27250             cls : 'x-paging-position  btn-outline-secondary',
27251              disabled: true,
27252             html : this.beforePageText  +
27253                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27254                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27255          } ); //?? escaped?
27256         
27257         this.field = field.el.select('input', true).first();
27258         this.field.on("keydown", this.onPagingKeydown, this);
27259         this.field.on("focus", function(){this.dom.select();});
27260     
27261     
27262         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27263         //this.field.setHeight(18);
27264         //this.addSeparator();
27265         this.next = this.navgroup.addItem({
27266             tooltip: this.nextText,
27267             cls: "next btn-outline-secondary",
27268             html : ' <i class="fa fa-forward"></i>',
27269             disabled: true,
27270             preventDefault: true,
27271             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27272         });
27273         this.last = this.navgroup.addItem({
27274             tooltip: this.lastText,
27275             html : ' <i class="fa fa-step-forward"></i>',
27276             cls: "next btn-outline-secondary",
27277             disabled: true,
27278             preventDefault: true,
27279             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27280         });
27281     //this.addSeparator();
27282         this.loading = this.navgroup.addItem({
27283             tooltip: this.refreshText,
27284             cls: "btn-outline-secondary",
27285             html : ' <i class="fa fa-refresh"></i>',
27286             preventDefault: true,
27287             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27288         });
27289         
27290     },
27291
27292     // private
27293     updateInfo : function(){
27294         if(this.displayEl){
27295             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27296             var msg = count == 0 ?
27297                 this.emptyMsg :
27298                 String.format(
27299                     this.displayMsg,
27300                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27301                 );
27302             this.displayEl.update(msg);
27303         }
27304     },
27305
27306     // private
27307     onLoad : function(ds, r, o)
27308     {
27309         this.cursor = o.params && o.params.start ? o.params.start : 0;
27310         
27311         var d = this.getPageData(),
27312             ap = d.activePage,
27313             ps = d.pages;
27314         
27315         
27316         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27317         this.field.dom.value = ap;
27318         this.first.setDisabled(ap == 1);
27319         this.prev.setDisabled(ap == 1);
27320         this.next.setDisabled(ap == ps);
27321         this.last.setDisabled(ap == ps);
27322         this.loading.enable();
27323         this.updateInfo();
27324     },
27325
27326     // private
27327     getPageData : function(){
27328         var total = this.ds.getTotalCount();
27329         return {
27330             total : total,
27331             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27332             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27333         };
27334     },
27335
27336     // private
27337     onLoadError : function(){
27338         this.loading.enable();
27339     },
27340
27341     // private
27342     onPagingKeydown : function(e){
27343         var k = e.getKey();
27344         var d = this.getPageData();
27345         if(k == e.RETURN){
27346             var v = this.field.dom.value, pageNum;
27347             if(!v || isNaN(pageNum = parseInt(v, 10))){
27348                 this.field.dom.value = d.activePage;
27349                 return;
27350             }
27351             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27352             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27353             e.stopEvent();
27354         }
27355         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))
27356         {
27357           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27358           this.field.dom.value = pageNum;
27359           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27360           e.stopEvent();
27361         }
27362         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27363         {
27364           var v = this.field.dom.value, pageNum; 
27365           var increment = (e.shiftKey) ? 10 : 1;
27366           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27367                 increment *= -1;
27368           }
27369           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27370             this.field.dom.value = d.activePage;
27371             return;
27372           }
27373           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27374           {
27375             this.field.dom.value = parseInt(v, 10) + increment;
27376             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27377             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27378           }
27379           e.stopEvent();
27380         }
27381     },
27382
27383     // private
27384     beforeLoad : function(){
27385         if(this.loading){
27386             this.loading.disable();
27387         }
27388     },
27389
27390     // private
27391     onClick : function(which){
27392         
27393         var ds = this.ds;
27394         if (!ds) {
27395             return;
27396         }
27397         
27398         switch(which){
27399             case "first":
27400                 ds.load({params:{start: 0, limit: this.pageSize}});
27401             break;
27402             case "prev":
27403                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27404             break;
27405             case "next":
27406                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27407             break;
27408             case "last":
27409                 var total = ds.getTotalCount();
27410                 var extra = total % this.pageSize;
27411                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27412                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27413             break;
27414             case "refresh":
27415                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27416             break;
27417         }
27418     },
27419
27420     /**
27421      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27422      * @param {Roo.data.Store} store The data store to unbind
27423      */
27424     unbind : function(ds){
27425         ds.un("beforeload", this.beforeLoad, this);
27426         ds.un("load", this.onLoad, this);
27427         ds.un("loadexception", this.onLoadError, this);
27428         ds.un("remove", this.updateInfo, this);
27429         ds.un("add", this.updateInfo, this);
27430         this.ds = undefined;
27431     },
27432
27433     /**
27434      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27435      * @param {Roo.data.Store} store The data store to bind
27436      */
27437     bind : function(ds){
27438         ds.on("beforeload", this.beforeLoad, this);
27439         ds.on("load", this.onLoad, this);
27440         ds.on("loadexception", this.onLoadError, this);
27441         ds.on("remove", this.updateInfo, this);
27442         ds.on("add", this.updateInfo, this);
27443         this.ds = ds;
27444     }
27445 });/*
27446  * - LGPL
27447  *
27448  * element
27449  * 
27450  */
27451
27452 /**
27453  * @class Roo.bootstrap.MessageBar
27454  * @extends Roo.bootstrap.Component
27455  * Bootstrap MessageBar class
27456  * @cfg {String} html contents of the MessageBar
27457  * @cfg {String} weight (info | success | warning | danger) default info
27458  * @cfg {String} beforeClass insert the bar before the given class
27459  * @cfg {Boolean} closable (true | false) default false
27460  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27461  * 
27462  * @constructor
27463  * Create a new Element
27464  * @param {Object} config The config object
27465  */
27466
27467 Roo.bootstrap.MessageBar = function(config){
27468     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27469 };
27470
27471 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27472     
27473     html: '',
27474     weight: 'info',
27475     closable: false,
27476     fixed: false,
27477     beforeClass: 'bootstrap-sticky-wrap',
27478     
27479     getAutoCreate : function(){
27480         
27481         var cfg = {
27482             tag: 'div',
27483             cls: 'alert alert-dismissable alert-' + this.weight,
27484             cn: [
27485                 {
27486                     tag: 'span',
27487                     cls: 'message',
27488                     html: this.html || ''
27489                 }
27490             ]
27491         };
27492         
27493         if(this.fixed){
27494             cfg.cls += ' alert-messages-fixed';
27495         }
27496         
27497         if(this.closable){
27498             cfg.cn.push({
27499                 tag: 'button',
27500                 cls: 'close',
27501                 html: 'x'
27502             });
27503         }
27504         
27505         return cfg;
27506     },
27507     
27508     onRender : function(ct, position)
27509     {
27510         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27511         
27512         if(!this.el){
27513             var cfg = Roo.apply({},  this.getAutoCreate());
27514             cfg.id = Roo.id();
27515             
27516             if (this.cls) {
27517                 cfg.cls += ' ' + this.cls;
27518             }
27519             if (this.style) {
27520                 cfg.style = this.style;
27521             }
27522             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27523             
27524             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27525         }
27526         
27527         this.el.select('>button.close').on('click', this.hide, this);
27528         
27529     },
27530     
27531     show : function()
27532     {
27533         if (!this.rendered) {
27534             this.render();
27535         }
27536         
27537         this.el.show();
27538         
27539         this.fireEvent('show', this);
27540         
27541     },
27542     
27543     hide : function()
27544     {
27545         if (!this.rendered) {
27546             this.render();
27547         }
27548         
27549         this.el.hide();
27550         
27551         this.fireEvent('hide', this);
27552     },
27553     
27554     update : function()
27555     {
27556 //        var e = this.el.dom.firstChild;
27557 //        
27558 //        if(this.closable){
27559 //            e = e.nextSibling;
27560 //        }
27561 //        
27562 //        e.data = this.html || '';
27563
27564         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27565     }
27566    
27567 });
27568
27569  
27570
27571      /*
27572  * - LGPL
27573  *
27574  * Graph
27575  * 
27576  */
27577
27578
27579 /**
27580  * @class Roo.bootstrap.Graph
27581  * @extends Roo.bootstrap.Component
27582  * Bootstrap Graph class
27583 > Prameters
27584  -sm {number} sm 4
27585  -md {number} md 5
27586  @cfg {String} graphtype  bar | vbar | pie
27587  @cfg {number} g_x coodinator | centre x (pie)
27588  @cfg {number} g_y coodinator | centre y (pie)
27589  @cfg {number} g_r radius (pie)
27590  @cfg {number} g_height height of the chart (respected by all elements in the set)
27591  @cfg {number} g_width width of the chart (respected by all elements in the set)
27592  @cfg {Object} title The title of the chart
27593     
27594  -{Array}  values
27595  -opts (object) options for the chart 
27596      o {
27597      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27598      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27599      o vgutter (number)
27600      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.
27601      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27602      o to
27603      o stretch (boolean)
27604      o }
27605  -opts (object) options for the pie
27606      o{
27607      o cut
27608      o startAngle (number)
27609      o endAngle (number)
27610      } 
27611  *
27612  * @constructor
27613  * Create a new Input
27614  * @param {Object} config The config object
27615  */
27616
27617 Roo.bootstrap.Graph = function(config){
27618     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27619     
27620     this.addEvents({
27621         // img events
27622         /**
27623          * @event click
27624          * The img click event for the img.
27625          * @param {Roo.EventObject} e
27626          */
27627         "click" : true
27628     });
27629 };
27630
27631 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27632     
27633     sm: 4,
27634     md: 5,
27635     graphtype: 'bar',
27636     g_height: 250,
27637     g_width: 400,
27638     g_x: 50,
27639     g_y: 50,
27640     g_r: 30,
27641     opts:{
27642         //g_colors: this.colors,
27643         g_type: 'soft',
27644         g_gutter: '20%'
27645
27646     },
27647     title : false,
27648
27649     getAutoCreate : function(){
27650         
27651         var cfg = {
27652             tag: 'div',
27653             html : null
27654         };
27655         
27656         
27657         return  cfg;
27658     },
27659
27660     onRender : function(ct,position){
27661         
27662         
27663         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27664         
27665         if (typeof(Raphael) == 'undefined') {
27666             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27667             return;
27668         }
27669         
27670         this.raphael = Raphael(this.el.dom);
27671         
27672                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27673                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27674                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27675                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27676                 /*
27677                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27678                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27679                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27680                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27681                 
27682                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27683                 r.barchart(330, 10, 300, 220, data1);
27684                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27685                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27686                 */
27687                 
27688                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27689                 // r.barchart(30, 30, 560, 250,  xdata, {
27690                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27691                 //     axis : "0 0 1 1",
27692                 //     axisxlabels :  xdata
27693                 //     //yvalues : cols,
27694                    
27695                 // });
27696 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27697 //        
27698 //        this.load(null,xdata,{
27699 //                axis : "0 0 1 1",
27700 //                axisxlabels :  xdata
27701 //                });
27702
27703     },
27704
27705     load : function(graphtype,xdata,opts)
27706     {
27707         this.raphael.clear();
27708         if(!graphtype) {
27709             graphtype = this.graphtype;
27710         }
27711         if(!opts){
27712             opts = this.opts;
27713         }
27714         var r = this.raphael,
27715             fin = function () {
27716                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27717             },
27718             fout = function () {
27719                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27720             },
27721             pfin = function() {
27722                 this.sector.stop();
27723                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27724
27725                 if (this.label) {
27726                     this.label[0].stop();
27727                     this.label[0].attr({ r: 7.5 });
27728                     this.label[1].attr({ "font-weight": 800 });
27729                 }
27730             },
27731             pfout = function() {
27732                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27733
27734                 if (this.label) {
27735                     this.label[0].animate({ r: 5 }, 500, "bounce");
27736                     this.label[1].attr({ "font-weight": 400 });
27737                 }
27738             };
27739
27740         switch(graphtype){
27741             case 'bar':
27742                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27743                 break;
27744             case 'hbar':
27745                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27746                 break;
27747             case 'pie':
27748 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27749 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27750 //            
27751                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27752                 
27753                 break;
27754
27755         }
27756         
27757         if(this.title){
27758             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27759         }
27760         
27761     },
27762     
27763     setTitle: function(o)
27764     {
27765         this.title = o;
27766     },
27767     
27768     initEvents: function() {
27769         
27770         if(!this.href){
27771             this.el.on('click', this.onClick, this);
27772         }
27773     },
27774     
27775     onClick : function(e)
27776     {
27777         Roo.log('img onclick');
27778         this.fireEvent('click', this, e);
27779     }
27780    
27781 });
27782
27783  
27784 /*
27785  * - LGPL
27786  *
27787  * numberBox
27788  * 
27789  */
27790 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27791
27792 /**
27793  * @class Roo.bootstrap.dash.NumberBox
27794  * @extends Roo.bootstrap.Component
27795  * Bootstrap NumberBox class
27796  * @cfg {String} headline Box headline
27797  * @cfg {String} content Box content
27798  * @cfg {String} icon Box icon
27799  * @cfg {String} footer Footer text
27800  * @cfg {String} fhref Footer href
27801  * 
27802  * @constructor
27803  * Create a new NumberBox
27804  * @param {Object} config The config object
27805  */
27806
27807
27808 Roo.bootstrap.dash.NumberBox = function(config){
27809     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27810     
27811 };
27812
27813 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27814     
27815     headline : '',
27816     content : '',
27817     icon : '',
27818     footer : '',
27819     fhref : '',
27820     ficon : '',
27821     
27822     getAutoCreate : function(){
27823         
27824         var cfg = {
27825             tag : 'div',
27826             cls : 'small-box ',
27827             cn : [
27828                 {
27829                     tag : 'div',
27830                     cls : 'inner',
27831                     cn :[
27832                         {
27833                             tag : 'h3',
27834                             cls : 'roo-headline',
27835                             html : this.headline
27836                         },
27837                         {
27838                             tag : 'p',
27839                             cls : 'roo-content',
27840                             html : this.content
27841                         }
27842                     ]
27843                 }
27844             ]
27845         };
27846         
27847         if(this.icon){
27848             cfg.cn.push({
27849                 tag : 'div',
27850                 cls : 'icon',
27851                 cn :[
27852                     {
27853                         tag : 'i',
27854                         cls : 'ion ' + this.icon
27855                     }
27856                 ]
27857             });
27858         }
27859         
27860         if(this.footer){
27861             var footer = {
27862                 tag : 'a',
27863                 cls : 'small-box-footer',
27864                 href : this.fhref || '#',
27865                 html : this.footer
27866             };
27867             
27868             cfg.cn.push(footer);
27869             
27870         }
27871         
27872         return  cfg;
27873     },
27874
27875     onRender : function(ct,position){
27876         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27877
27878
27879        
27880                 
27881     },
27882
27883     setHeadline: function (value)
27884     {
27885         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27886     },
27887     
27888     setFooter: function (value, href)
27889     {
27890         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27891         
27892         if(href){
27893             this.el.select('a.small-box-footer',true).first().attr('href', href);
27894         }
27895         
27896     },
27897
27898     setContent: function (value)
27899     {
27900         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27901     },
27902
27903     initEvents: function() 
27904     {   
27905         
27906     }
27907     
27908 });
27909
27910  
27911 /*
27912  * - LGPL
27913  *
27914  * TabBox
27915  * 
27916  */
27917 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27918
27919 /**
27920  * @class Roo.bootstrap.dash.TabBox
27921  * @extends Roo.bootstrap.Component
27922  * Bootstrap TabBox class
27923  * @cfg {String} title Title of the TabBox
27924  * @cfg {String} icon Icon of the TabBox
27925  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27926  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27927  * 
27928  * @constructor
27929  * Create a new TabBox
27930  * @param {Object} config The config object
27931  */
27932
27933
27934 Roo.bootstrap.dash.TabBox = function(config){
27935     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27936     this.addEvents({
27937         // raw events
27938         /**
27939          * @event addpane
27940          * When a pane is added
27941          * @param {Roo.bootstrap.dash.TabPane} pane
27942          */
27943         "addpane" : true,
27944         /**
27945          * @event activatepane
27946          * When a pane is activated
27947          * @param {Roo.bootstrap.dash.TabPane} pane
27948          */
27949         "activatepane" : true
27950         
27951          
27952     });
27953     
27954     this.panes = [];
27955 };
27956
27957 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27958
27959     title : '',
27960     icon : false,
27961     showtabs : true,
27962     tabScrollable : false,
27963     
27964     getChildContainer : function()
27965     {
27966         return this.el.select('.tab-content', true).first();
27967     },
27968     
27969     getAutoCreate : function(){
27970         
27971         var header = {
27972             tag: 'li',
27973             cls: 'pull-left header',
27974             html: this.title,
27975             cn : []
27976         };
27977         
27978         if(this.icon){
27979             header.cn.push({
27980                 tag: 'i',
27981                 cls: 'fa ' + this.icon
27982             });
27983         }
27984         
27985         var h = {
27986             tag: 'ul',
27987             cls: 'nav nav-tabs pull-right',
27988             cn: [
27989                 header
27990             ]
27991         };
27992         
27993         if(this.tabScrollable){
27994             h = {
27995                 tag: 'div',
27996                 cls: 'tab-header',
27997                 cn: [
27998                     {
27999                         tag: 'ul',
28000                         cls: 'nav nav-tabs pull-right',
28001                         cn: [
28002                             header
28003                         ]
28004                     }
28005                 ]
28006             };
28007         }
28008         
28009         var cfg = {
28010             tag: 'div',
28011             cls: 'nav-tabs-custom',
28012             cn: [
28013                 h,
28014                 {
28015                     tag: 'div',
28016                     cls: 'tab-content no-padding',
28017                     cn: []
28018                 }
28019             ]
28020         };
28021
28022         return  cfg;
28023     },
28024     initEvents : function()
28025     {
28026         //Roo.log('add add pane handler');
28027         this.on('addpane', this.onAddPane, this);
28028     },
28029      /**
28030      * Updates the box title
28031      * @param {String} html to set the title to.
28032      */
28033     setTitle : function(value)
28034     {
28035         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28036     },
28037     onAddPane : function(pane)
28038     {
28039         this.panes.push(pane);
28040         //Roo.log('addpane');
28041         //Roo.log(pane);
28042         // tabs are rendere left to right..
28043         if(!this.showtabs){
28044             return;
28045         }
28046         
28047         var ctr = this.el.select('.nav-tabs', true).first();
28048          
28049          
28050         var existing = ctr.select('.nav-tab',true);
28051         var qty = existing.getCount();;
28052         
28053         
28054         var tab = ctr.createChild({
28055             tag : 'li',
28056             cls : 'nav-tab' + (qty ? '' : ' active'),
28057             cn : [
28058                 {
28059                     tag : 'a',
28060                     href:'#',
28061                     html : pane.title
28062                 }
28063             ]
28064         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28065         pane.tab = tab;
28066         
28067         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28068         if (!qty) {
28069             pane.el.addClass('active');
28070         }
28071         
28072                 
28073     },
28074     onTabClick : function(ev,un,ob,pane)
28075     {
28076         //Roo.log('tab - prev default');
28077         ev.preventDefault();
28078         
28079         
28080         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28081         pane.tab.addClass('active');
28082         //Roo.log(pane.title);
28083         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28084         // technically we should have a deactivate event.. but maybe add later.
28085         // and it should not de-activate the selected tab...
28086         this.fireEvent('activatepane', pane);
28087         pane.el.addClass('active');
28088         pane.fireEvent('activate');
28089         
28090         
28091     },
28092     
28093     getActivePane : function()
28094     {
28095         var r = false;
28096         Roo.each(this.panes, function(p) {
28097             if(p.el.hasClass('active')){
28098                 r = p;
28099                 return false;
28100             }
28101             
28102             return;
28103         });
28104         
28105         return r;
28106     }
28107     
28108     
28109 });
28110
28111  
28112 /*
28113  * - LGPL
28114  *
28115  * Tab pane
28116  * 
28117  */
28118 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28119 /**
28120  * @class Roo.bootstrap.TabPane
28121  * @extends Roo.bootstrap.Component
28122  * Bootstrap TabPane class
28123  * @cfg {Boolean} active (false | true) Default false
28124  * @cfg {String} title title of panel
28125
28126  * 
28127  * @constructor
28128  * Create a new TabPane
28129  * @param {Object} config The config object
28130  */
28131
28132 Roo.bootstrap.dash.TabPane = function(config){
28133     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28134     
28135     this.addEvents({
28136         // raw events
28137         /**
28138          * @event activate
28139          * When a pane is activated
28140          * @param {Roo.bootstrap.dash.TabPane} pane
28141          */
28142         "activate" : true
28143          
28144     });
28145 };
28146
28147 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28148     
28149     active : false,
28150     title : '',
28151     
28152     // the tabBox that this is attached to.
28153     tab : false,
28154      
28155     getAutoCreate : function() 
28156     {
28157         var cfg = {
28158             tag: 'div',
28159             cls: 'tab-pane'
28160         };
28161         
28162         if(this.active){
28163             cfg.cls += ' active';
28164         }
28165         
28166         return cfg;
28167     },
28168     initEvents  : function()
28169     {
28170         //Roo.log('trigger add pane handler');
28171         this.parent().fireEvent('addpane', this)
28172     },
28173     
28174      /**
28175      * Updates the tab title 
28176      * @param {String} html to set the title to.
28177      */
28178     setTitle: function(str)
28179     {
28180         if (!this.tab) {
28181             return;
28182         }
28183         this.title = str;
28184         this.tab.select('a', true).first().dom.innerHTML = str;
28185         
28186     }
28187     
28188     
28189     
28190 });
28191
28192  
28193
28194
28195  /*
28196  * - LGPL
28197  *
28198  * menu
28199  * 
28200  */
28201 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28202
28203 /**
28204  * @class Roo.bootstrap.menu.Menu
28205  * @extends Roo.bootstrap.Component
28206  * Bootstrap Menu class - container for Menu
28207  * @cfg {String} html Text of the menu
28208  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28209  * @cfg {String} icon Font awesome icon
28210  * @cfg {String} pos Menu align to (top | bottom) default bottom
28211  * 
28212  * 
28213  * @constructor
28214  * Create a new Menu
28215  * @param {Object} config The config object
28216  */
28217
28218
28219 Roo.bootstrap.menu.Menu = function(config){
28220     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28221     
28222     this.addEvents({
28223         /**
28224          * @event beforeshow
28225          * Fires before this menu is displayed
28226          * @param {Roo.bootstrap.menu.Menu} this
28227          */
28228         beforeshow : true,
28229         /**
28230          * @event beforehide
28231          * Fires before this menu is hidden
28232          * @param {Roo.bootstrap.menu.Menu} this
28233          */
28234         beforehide : true,
28235         /**
28236          * @event show
28237          * Fires after this menu is displayed
28238          * @param {Roo.bootstrap.menu.Menu} this
28239          */
28240         show : true,
28241         /**
28242          * @event hide
28243          * Fires after this menu is hidden
28244          * @param {Roo.bootstrap.menu.Menu} this
28245          */
28246         hide : true,
28247         /**
28248          * @event click
28249          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28250          * @param {Roo.bootstrap.menu.Menu} this
28251          * @param {Roo.EventObject} e
28252          */
28253         click : true
28254     });
28255     
28256 };
28257
28258 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28259     
28260     submenu : false,
28261     html : '',
28262     weight : 'default',
28263     icon : false,
28264     pos : 'bottom',
28265     
28266     
28267     getChildContainer : function() {
28268         if(this.isSubMenu){
28269             return this.el;
28270         }
28271         
28272         return this.el.select('ul.dropdown-menu', true).first();  
28273     },
28274     
28275     getAutoCreate : function()
28276     {
28277         var text = [
28278             {
28279                 tag : 'span',
28280                 cls : 'roo-menu-text',
28281                 html : this.html
28282             }
28283         ];
28284         
28285         if(this.icon){
28286             text.unshift({
28287                 tag : 'i',
28288                 cls : 'fa ' + this.icon
28289             })
28290         }
28291         
28292         
28293         var cfg = {
28294             tag : 'div',
28295             cls : 'btn-group',
28296             cn : [
28297                 {
28298                     tag : 'button',
28299                     cls : 'dropdown-button btn btn-' + this.weight,
28300                     cn : text
28301                 },
28302                 {
28303                     tag : 'button',
28304                     cls : 'dropdown-toggle btn btn-' + this.weight,
28305                     cn : [
28306                         {
28307                             tag : 'span',
28308                             cls : 'caret'
28309                         }
28310                     ]
28311                 },
28312                 {
28313                     tag : 'ul',
28314                     cls : 'dropdown-menu'
28315                 }
28316             ]
28317             
28318         };
28319         
28320         if(this.pos == 'top'){
28321             cfg.cls += ' dropup';
28322         }
28323         
28324         if(this.isSubMenu){
28325             cfg = {
28326                 tag : 'ul',
28327                 cls : 'dropdown-menu'
28328             }
28329         }
28330         
28331         return cfg;
28332     },
28333     
28334     onRender : function(ct, position)
28335     {
28336         this.isSubMenu = ct.hasClass('dropdown-submenu');
28337         
28338         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28339     },
28340     
28341     initEvents : function() 
28342     {
28343         if(this.isSubMenu){
28344             return;
28345         }
28346         
28347         this.hidden = true;
28348         
28349         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28350         this.triggerEl.on('click', this.onTriggerPress, this);
28351         
28352         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28353         this.buttonEl.on('click', this.onClick, this);
28354         
28355     },
28356     
28357     list : function()
28358     {
28359         if(this.isSubMenu){
28360             return this.el;
28361         }
28362         
28363         return this.el.select('ul.dropdown-menu', true).first();
28364     },
28365     
28366     onClick : function(e)
28367     {
28368         this.fireEvent("click", this, e);
28369     },
28370     
28371     onTriggerPress  : function(e)
28372     {   
28373         if (this.isVisible()) {
28374             this.hide();
28375         } else {
28376             this.show();
28377         }
28378     },
28379     
28380     isVisible : function(){
28381         return !this.hidden;
28382     },
28383     
28384     show : function()
28385     {
28386         this.fireEvent("beforeshow", this);
28387         
28388         this.hidden = false;
28389         this.el.addClass('open');
28390         
28391         Roo.get(document).on("mouseup", this.onMouseUp, this);
28392         
28393         this.fireEvent("show", this);
28394         
28395         
28396     },
28397     
28398     hide : function()
28399     {
28400         this.fireEvent("beforehide", this);
28401         
28402         this.hidden = true;
28403         this.el.removeClass('open');
28404         
28405         Roo.get(document).un("mouseup", this.onMouseUp);
28406         
28407         this.fireEvent("hide", this);
28408     },
28409     
28410     onMouseUp : function()
28411     {
28412         this.hide();
28413     }
28414     
28415 });
28416
28417  
28418  /*
28419  * - LGPL
28420  *
28421  * menu item
28422  * 
28423  */
28424 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28425
28426 /**
28427  * @class Roo.bootstrap.menu.Item
28428  * @extends Roo.bootstrap.Component
28429  * Bootstrap MenuItem class
28430  * @cfg {Boolean} submenu (true | false) default false
28431  * @cfg {String} html text of the item
28432  * @cfg {String} href the link
28433  * @cfg {Boolean} disable (true | false) default false
28434  * @cfg {Boolean} preventDefault (true | false) default true
28435  * @cfg {String} icon Font awesome icon
28436  * @cfg {String} pos Submenu align to (left | right) default right 
28437  * 
28438  * 
28439  * @constructor
28440  * Create a new Item
28441  * @param {Object} config The config object
28442  */
28443
28444
28445 Roo.bootstrap.menu.Item = function(config){
28446     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28447     this.addEvents({
28448         /**
28449          * @event mouseover
28450          * Fires when the mouse is hovering over this menu
28451          * @param {Roo.bootstrap.menu.Item} this
28452          * @param {Roo.EventObject} e
28453          */
28454         mouseover : true,
28455         /**
28456          * @event mouseout
28457          * Fires when the mouse exits this menu
28458          * @param {Roo.bootstrap.menu.Item} this
28459          * @param {Roo.EventObject} e
28460          */
28461         mouseout : true,
28462         // raw events
28463         /**
28464          * @event click
28465          * The raw click event for the entire grid.
28466          * @param {Roo.EventObject} e
28467          */
28468         click : true
28469     });
28470 };
28471
28472 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28473     
28474     submenu : false,
28475     href : '',
28476     html : '',
28477     preventDefault: true,
28478     disable : false,
28479     icon : false,
28480     pos : 'right',
28481     
28482     getAutoCreate : function()
28483     {
28484         var text = [
28485             {
28486                 tag : 'span',
28487                 cls : 'roo-menu-item-text',
28488                 html : this.html
28489             }
28490         ];
28491         
28492         if(this.icon){
28493             text.unshift({
28494                 tag : 'i',
28495                 cls : 'fa ' + this.icon
28496             })
28497         }
28498         
28499         var cfg = {
28500             tag : 'li',
28501             cn : [
28502                 {
28503                     tag : 'a',
28504                     href : this.href || '#',
28505                     cn : text
28506                 }
28507             ]
28508         };
28509         
28510         if(this.disable){
28511             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28512         }
28513         
28514         if(this.submenu){
28515             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28516             
28517             if(this.pos == 'left'){
28518                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28519             }
28520         }
28521         
28522         return cfg;
28523     },
28524     
28525     initEvents : function() 
28526     {
28527         this.el.on('mouseover', this.onMouseOver, this);
28528         this.el.on('mouseout', this.onMouseOut, this);
28529         
28530         this.el.select('a', true).first().on('click', this.onClick, this);
28531         
28532     },
28533     
28534     onClick : function(e)
28535     {
28536         if(this.preventDefault){
28537             e.preventDefault();
28538         }
28539         
28540         this.fireEvent("click", this, e);
28541     },
28542     
28543     onMouseOver : function(e)
28544     {
28545         if(this.submenu && this.pos == 'left'){
28546             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28547         }
28548         
28549         this.fireEvent("mouseover", this, e);
28550     },
28551     
28552     onMouseOut : function(e)
28553     {
28554         this.fireEvent("mouseout", this, e);
28555     }
28556 });
28557
28558  
28559
28560  /*
28561  * - LGPL
28562  *
28563  * menu separator
28564  * 
28565  */
28566 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28567
28568 /**
28569  * @class Roo.bootstrap.menu.Separator
28570  * @extends Roo.bootstrap.Component
28571  * Bootstrap Separator class
28572  * 
28573  * @constructor
28574  * Create a new Separator
28575  * @param {Object} config The config object
28576  */
28577
28578
28579 Roo.bootstrap.menu.Separator = function(config){
28580     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28581 };
28582
28583 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28584     
28585     getAutoCreate : function(){
28586         var cfg = {
28587             tag : 'li',
28588             cls: 'divider'
28589         };
28590         
28591         return cfg;
28592     }
28593    
28594 });
28595
28596  
28597
28598  /*
28599  * - LGPL
28600  *
28601  * Tooltip
28602  * 
28603  */
28604
28605 /**
28606  * @class Roo.bootstrap.Tooltip
28607  * Bootstrap Tooltip class
28608  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28609  * to determine which dom element triggers the tooltip.
28610  * 
28611  * It needs to add support for additional attributes like tooltip-position
28612  * 
28613  * @constructor
28614  * Create a new Toolti
28615  * @param {Object} config The config object
28616  */
28617
28618 Roo.bootstrap.Tooltip = function(config){
28619     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28620     
28621     this.alignment = Roo.bootstrap.Tooltip.alignment;
28622     
28623     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28624         this.alignment = config.alignment;
28625     }
28626     
28627 };
28628
28629 Roo.apply(Roo.bootstrap.Tooltip, {
28630     /**
28631      * @function init initialize tooltip monitoring.
28632      * @static
28633      */
28634     currentEl : false,
28635     currentTip : false,
28636     currentRegion : false,
28637     
28638     //  init : delay?
28639     
28640     init : function()
28641     {
28642         Roo.get(document).on('mouseover', this.enter ,this);
28643         Roo.get(document).on('mouseout', this.leave, this);
28644          
28645         
28646         this.currentTip = new Roo.bootstrap.Tooltip();
28647     },
28648     
28649     enter : function(ev)
28650     {
28651         var dom = ev.getTarget();
28652         
28653         //Roo.log(['enter',dom]);
28654         var el = Roo.fly(dom);
28655         if (this.currentEl) {
28656             //Roo.log(dom);
28657             //Roo.log(this.currentEl);
28658             //Roo.log(this.currentEl.contains(dom));
28659             if (this.currentEl == el) {
28660                 return;
28661             }
28662             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28663                 return;
28664             }
28665
28666         }
28667         
28668         if (this.currentTip.el) {
28669             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28670         }    
28671         //Roo.log(ev);
28672         
28673         if(!el || el.dom == document){
28674             return;
28675         }
28676         
28677         var bindEl = el;
28678         
28679         // you can not look for children, as if el is the body.. then everythign is the child..
28680         if (!el.attr('tooltip')) { //
28681             if (!el.select("[tooltip]").elements.length) {
28682                 return;
28683             }
28684             // is the mouse over this child...?
28685             bindEl = el.select("[tooltip]").first();
28686             var xy = ev.getXY();
28687             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28688                 //Roo.log("not in region.");
28689                 return;
28690             }
28691             //Roo.log("child element over..");
28692             
28693         }
28694         this.currentEl = bindEl;
28695         this.currentTip.bind(bindEl);
28696         this.currentRegion = Roo.lib.Region.getRegion(dom);
28697         this.currentTip.enter();
28698         
28699     },
28700     leave : function(ev)
28701     {
28702         var dom = ev.getTarget();
28703         //Roo.log(['leave',dom]);
28704         if (!this.currentEl) {
28705             return;
28706         }
28707         
28708         
28709         if (dom != this.currentEl.dom) {
28710             return;
28711         }
28712         var xy = ev.getXY();
28713         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28714             return;
28715         }
28716         // only activate leave if mouse cursor is outside... bounding box..
28717         
28718         
28719         
28720         
28721         if (this.currentTip) {
28722             this.currentTip.leave();
28723         }
28724         //Roo.log('clear currentEl');
28725         this.currentEl = false;
28726         
28727         
28728     },
28729     alignment : {
28730         'left' : ['r-l', [-2,0], 'right'],
28731         'right' : ['l-r', [2,0], 'left'],
28732         'bottom' : ['t-b', [0,2], 'top'],
28733         'top' : [ 'b-t', [0,-2], 'bottom']
28734     }
28735     
28736 });
28737
28738
28739 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28740     
28741     
28742     bindEl : false,
28743     
28744     delay : null, // can be { show : 300 , hide: 500}
28745     
28746     timeout : null,
28747     
28748     hoverState : null, //???
28749     
28750     placement : 'bottom', 
28751     
28752     alignment : false,
28753     
28754     getAutoCreate : function(){
28755     
28756         var cfg = {
28757            cls : 'tooltip',   
28758            role : 'tooltip',
28759            cn : [
28760                 {
28761                     cls : 'tooltip-arrow arrow'
28762                 },
28763                 {
28764                     cls : 'tooltip-inner'
28765                 }
28766            ]
28767         };
28768         
28769         return cfg;
28770     },
28771     bind : function(el)
28772     {
28773         this.bindEl = el;
28774     },
28775     
28776     initEvents : function()
28777     {
28778         this.arrowEl = this.el.select('.arrow', true).first();
28779         this.innerEl = this.el.select('.tooltip-inner', true).first();
28780     },
28781     
28782     enter : function () {
28783        
28784         if (this.timeout != null) {
28785             clearTimeout(this.timeout);
28786         }
28787         
28788         this.hoverState = 'in';
28789          //Roo.log("enter - show");
28790         if (!this.delay || !this.delay.show) {
28791             this.show();
28792             return;
28793         }
28794         var _t = this;
28795         this.timeout = setTimeout(function () {
28796             if (_t.hoverState == 'in') {
28797                 _t.show();
28798             }
28799         }, this.delay.show);
28800     },
28801     leave : function()
28802     {
28803         clearTimeout(this.timeout);
28804     
28805         this.hoverState = 'out';
28806          if (!this.delay || !this.delay.hide) {
28807             this.hide();
28808             return;
28809         }
28810        
28811         var _t = this;
28812         this.timeout = setTimeout(function () {
28813             //Roo.log("leave - timeout");
28814             
28815             if (_t.hoverState == 'out') {
28816                 _t.hide();
28817                 Roo.bootstrap.Tooltip.currentEl = false;
28818             }
28819         }, delay);
28820     },
28821     
28822     show : function (msg)
28823     {
28824         if (!this.el) {
28825             this.render(document.body);
28826         }
28827         // set content.
28828         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28829         
28830         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28831         
28832         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28833         
28834         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28835                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28836         
28837         var placement = typeof this.placement == 'function' ?
28838             this.placement.call(this, this.el, on_el) :
28839             this.placement;
28840             
28841         var autoToken = /\s?auto?\s?/i;
28842         var autoPlace = autoToken.test(placement);
28843         if (autoPlace) {
28844             placement = placement.replace(autoToken, '') || 'top';
28845         }
28846         
28847         //this.el.detach()
28848         //this.el.setXY([0,0]);
28849         this.el.show();
28850         //this.el.dom.style.display='block';
28851         
28852         //this.el.appendTo(on_el);
28853         
28854         var p = this.getPosition();
28855         var box = this.el.getBox();
28856         
28857         if (autoPlace) {
28858             // fixme..
28859         }
28860         
28861         var align = this.alignment[placement];
28862         
28863         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28864         
28865         if(placement == 'top' || placement == 'bottom'){
28866             if(xy[0] < 0){
28867                 placement = 'right';
28868             }
28869             
28870             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28871                 placement = 'left';
28872             }
28873             
28874             var scroll = Roo.select('body', true).first().getScroll();
28875             
28876             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28877                 placement = 'top';
28878             }
28879             
28880             align = this.alignment[placement];
28881             
28882             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28883             
28884         }
28885         
28886         this.el.alignTo(this.bindEl, align[0],align[1]);
28887         //var arrow = this.el.select('.arrow',true).first();
28888         //arrow.set(align[2], 
28889         
28890         this.el.addClass(placement);
28891         this.el.addClass("bs-tooltip-"+ placement);
28892         
28893         this.el.addClass('in fade show');
28894         
28895         this.hoverState = null;
28896         
28897         if (this.el.hasClass('fade')) {
28898             // fade it?
28899         }
28900         
28901         
28902         
28903         
28904         
28905     },
28906     hide : function()
28907     {
28908          
28909         if (!this.el) {
28910             return;
28911         }
28912         //this.el.setXY([0,0]);
28913         this.el.removeClass(['show', 'in']);
28914         //this.el.hide();
28915         
28916     }
28917     
28918 });
28919  
28920
28921  /*
28922  * - LGPL
28923  *
28924  * Location Picker
28925  * 
28926  */
28927
28928 /**
28929  * @class Roo.bootstrap.LocationPicker
28930  * @extends Roo.bootstrap.Component
28931  * Bootstrap LocationPicker class
28932  * @cfg {Number} latitude Position when init default 0
28933  * @cfg {Number} longitude Position when init default 0
28934  * @cfg {Number} zoom default 15
28935  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28936  * @cfg {Boolean} mapTypeControl default false
28937  * @cfg {Boolean} disableDoubleClickZoom default false
28938  * @cfg {Boolean} scrollwheel default true
28939  * @cfg {Boolean} streetViewControl default false
28940  * @cfg {Number} radius default 0
28941  * @cfg {String} locationName
28942  * @cfg {Boolean} draggable default true
28943  * @cfg {Boolean} enableAutocomplete default false
28944  * @cfg {Boolean} enableReverseGeocode default true
28945  * @cfg {String} markerTitle
28946  * 
28947  * @constructor
28948  * Create a new LocationPicker
28949  * @param {Object} config The config object
28950  */
28951
28952
28953 Roo.bootstrap.LocationPicker = function(config){
28954     
28955     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28956     
28957     this.addEvents({
28958         /**
28959          * @event initial
28960          * Fires when the picker initialized.
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          * @param {Google Location} location
28963          */
28964         initial : true,
28965         /**
28966          * @event positionchanged
28967          * Fires when the picker position changed.
28968          * @param {Roo.bootstrap.LocationPicker} this
28969          * @param {Google Location} location
28970          */
28971         positionchanged : true,
28972         /**
28973          * @event resize
28974          * Fires when the map resize.
28975          * @param {Roo.bootstrap.LocationPicker} this
28976          */
28977         resize : true,
28978         /**
28979          * @event show
28980          * Fires when the map show.
28981          * @param {Roo.bootstrap.LocationPicker} this
28982          */
28983         show : true,
28984         /**
28985          * @event hide
28986          * Fires when the map hide.
28987          * @param {Roo.bootstrap.LocationPicker} this
28988          */
28989         hide : true,
28990         /**
28991          * @event mapClick
28992          * Fires when click the map.
28993          * @param {Roo.bootstrap.LocationPicker} this
28994          * @param {Map event} e
28995          */
28996         mapClick : true,
28997         /**
28998          * @event mapRightClick
28999          * Fires when right click the map.
29000          * @param {Roo.bootstrap.LocationPicker} this
29001          * @param {Map event} e
29002          */
29003         mapRightClick : true,
29004         /**
29005          * @event markerClick
29006          * Fires when click the marker.
29007          * @param {Roo.bootstrap.LocationPicker} this
29008          * @param {Map event} e
29009          */
29010         markerClick : true,
29011         /**
29012          * @event markerRightClick
29013          * Fires when right click the marker.
29014          * @param {Roo.bootstrap.LocationPicker} this
29015          * @param {Map event} e
29016          */
29017         markerRightClick : true,
29018         /**
29019          * @event OverlayViewDraw
29020          * Fires when OverlayView Draw
29021          * @param {Roo.bootstrap.LocationPicker} this
29022          */
29023         OverlayViewDraw : true,
29024         /**
29025          * @event OverlayViewOnAdd
29026          * Fires when OverlayView Draw
29027          * @param {Roo.bootstrap.LocationPicker} this
29028          */
29029         OverlayViewOnAdd : true,
29030         /**
29031          * @event OverlayViewOnRemove
29032          * Fires when OverlayView Draw
29033          * @param {Roo.bootstrap.LocationPicker} this
29034          */
29035         OverlayViewOnRemove : true,
29036         /**
29037          * @event OverlayViewShow
29038          * Fires when OverlayView Draw
29039          * @param {Roo.bootstrap.LocationPicker} this
29040          * @param {Pixel} cpx
29041          */
29042         OverlayViewShow : true,
29043         /**
29044          * @event OverlayViewHide
29045          * Fires when OverlayView Draw
29046          * @param {Roo.bootstrap.LocationPicker} this
29047          */
29048         OverlayViewHide : true,
29049         /**
29050          * @event loadexception
29051          * Fires when load google lib failed.
29052          * @param {Roo.bootstrap.LocationPicker} this
29053          */
29054         loadexception : true
29055     });
29056         
29057 };
29058
29059 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29060     
29061     gMapContext: false,
29062     
29063     latitude: 0,
29064     longitude: 0,
29065     zoom: 15,
29066     mapTypeId: false,
29067     mapTypeControl: false,
29068     disableDoubleClickZoom: false,
29069     scrollwheel: true,
29070     streetViewControl: false,
29071     radius: 0,
29072     locationName: '',
29073     draggable: true,
29074     enableAutocomplete: false,
29075     enableReverseGeocode: true,
29076     markerTitle: '',
29077     
29078     getAutoCreate: function()
29079     {
29080
29081         var cfg = {
29082             tag: 'div',
29083             cls: 'roo-location-picker'
29084         };
29085         
29086         return cfg
29087     },
29088     
29089     initEvents: function(ct, position)
29090     {       
29091         if(!this.el.getWidth() || this.isApplied()){
29092             return;
29093         }
29094         
29095         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29096         
29097         this.initial();
29098     },
29099     
29100     initial: function()
29101     {
29102         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29103             this.fireEvent('loadexception', this);
29104             return;
29105         }
29106         
29107         if(!this.mapTypeId){
29108             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29109         }
29110         
29111         this.gMapContext = this.GMapContext();
29112         
29113         this.initOverlayView();
29114         
29115         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29116         
29117         var _this = this;
29118                 
29119         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29120             _this.setPosition(_this.gMapContext.marker.position);
29121         });
29122         
29123         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29124             _this.fireEvent('mapClick', this, event);
29125             
29126         });
29127
29128         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29129             _this.fireEvent('mapRightClick', this, event);
29130             
29131         });
29132         
29133         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29134             _this.fireEvent('markerClick', this, event);
29135             
29136         });
29137
29138         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29139             _this.fireEvent('markerRightClick', this, event);
29140             
29141         });
29142         
29143         this.setPosition(this.gMapContext.location);
29144         
29145         this.fireEvent('initial', this, this.gMapContext.location);
29146     },
29147     
29148     initOverlayView: function()
29149     {
29150         var _this = this;
29151         
29152         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29153             
29154             draw: function()
29155             {
29156                 _this.fireEvent('OverlayViewDraw', _this);
29157             },
29158             
29159             onAdd: function()
29160             {
29161                 _this.fireEvent('OverlayViewOnAdd', _this);
29162             },
29163             
29164             onRemove: function()
29165             {
29166                 _this.fireEvent('OverlayViewOnRemove', _this);
29167             },
29168             
29169             show: function(cpx)
29170             {
29171                 _this.fireEvent('OverlayViewShow', _this, cpx);
29172             },
29173             
29174             hide: function()
29175             {
29176                 _this.fireEvent('OverlayViewHide', _this);
29177             }
29178             
29179         });
29180     },
29181     
29182     fromLatLngToContainerPixel: function(event)
29183     {
29184         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29185     },
29186     
29187     isApplied: function() 
29188     {
29189         return this.getGmapContext() == false ? false : true;
29190     },
29191     
29192     getGmapContext: function() 
29193     {
29194         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29195     },
29196     
29197     GMapContext: function() 
29198     {
29199         var position = new google.maps.LatLng(this.latitude, this.longitude);
29200         
29201         var _map = new google.maps.Map(this.el.dom, {
29202             center: position,
29203             zoom: this.zoom,
29204             mapTypeId: this.mapTypeId,
29205             mapTypeControl: this.mapTypeControl,
29206             disableDoubleClickZoom: this.disableDoubleClickZoom,
29207             scrollwheel: this.scrollwheel,
29208             streetViewControl: this.streetViewControl,
29209             locationName: this.locationName,
29210             draggable: this.draggable,
29211             enableAutocomplete: this.enableAutocomplete,
29212             enableReverseGeocode: this.enableReverseGeocode
29213         });
29214         
29215         var _marker = new google.maps.Marker({
29216             position: position,
29217             map: _map,
29218             title: this.markerTitle,
29219             draggable: this.draggable
29220         });
29221         
29222         return {
29223             map: _map,
29224             marker: _marker,
29225             circle: null,
29226             location: position,
29227             radius: this.radius,
29228             locationName: this.locationName,
29229             addressComponents: {
29230                 formatted_address: null,
29231                 addressLine1: null,
29232                 addressLine2: null,
29233                 streetName: null,
29234                 streetNumber: null,
29235                 city: null,
29236                 district: null,
29237                 state: null,
29238                 stateOrProvince: null
29239             },
29240             settings: this,
29241             domContainer: this.el.dom,
29242             geodecoder: new google.maps.Geocoder()
29243         };
29244     },
29245     
29246     drawCircle: function(center, radius, options) 
29247     {
29248         if (this.gMapContext.circle != null) {
29249             this.gMapContext.circle.setMap(null);
29250         }
29251         if (radius > 0) {
29252             radius *= 1;
29253             options = Roo.apply({}, options, {
29254                 strokeColor: "#0000FF",
29255                 strokeOpacity: .35,
29256                 strokeWeight: 2,
29257                 fillColor: "#0000FF",
29258                 fillOpacity: .2
29259             });
29260             
29261             options.map = this.gMapContext.map;
29262             options.radius = radius;
29263             options.center = center;
29264             this.gMapContext.circle = new google.maps.Circle(options);
29265             return this.gMapContext.circle;
29266         }
29267         
29268         return null;
29269     },
29270     
29271     setPosition: function(location) 
29272     {
29273         this.gMapContext.location = location;
29274         this.gMapContext.marker.setPosition(location);
29275         this.gMapContext.map.panTo(location);
29276         this.drawCircle(location, this.gMapContext.radius, {});
29277         
29278         var _this = this;
29279         
29280         if (this.gMapContext.settings.enableReverseGeocode) {
29281             this.gMapContext.geodecoder.geocode({
29282                 latLng: this.gMapContext.location
29283             }, function(results, status) {
29284                 
29285                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29286                     _this.gMapContext.locationName = results[0].formatted_address;
29287                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29288                     
29289                     _this.fireEvent('positionchanged', this, location);
29290                 }
29291             });
29292             
29293             return;
29294         }
29295         
29296         this.fireEvent('positionchanged', this, location);
29297     },
29298     
29299     resize: function()
29300     {
29301         google.maps.event.trigger(this.gMapContext.map, "resize");
29302         
29303         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29304         
29305         this.fireEvent('resize', this);
29306     },
29307     
29308     setPositionByLatLng: function(latitude, longitude)
29309     {
29310         this.setPosition(new google.maps.LatLng(latitude, longitude));
29311     },
29312     
29313     getCurrentPosition: function() 
29314     {
29315         return {
29316             latitude: this.gMapContext.location.lat(),
29317             longitude: this.gMapContext.location.lng()
29318         };
29319     },
29320     
29321     getAddressName: function() 
29322     {
29323         return this.gMapContext.locationName;
29324     },
29325     
29326     getAddressComponents: function() 
29327     {
29328         return this.gMapContext.addressComponents;
29329     },
29330     
29331     address_component_from_google_geocode: function(address_components) 
29332     {
29333         var result = {};
29334         
29335         for (var i = 0; i < address_components.length; i++) {
29336             var component = address_components[i];
29337             if (component.types.indexOf("postal_code") >= 0) {
29338                 result.postalCode = component.short_name;
29339             } else if (component.types.indexOf("street_number") >= 0) {
29340                 result.streetNumber = component.short_name;
29341             } else if (component.types.indexOf("route") >= 0) {
29342                 result.streetName = component.short_name;
29343             } else if (component.types.indexOf("neighborhood") >= 0) {
29344                 result.city = component.short_name;
29345             } else if (component.types.indexOf("locality") >= 0) {
29346                 result.city = component.short_name;
29347             } else if (component.types.indexOf("sublocality") >= 0) {
29348                 result.district = component.short_name;
29349             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29350                 result.stateOrProvince = component.short_name;
29351             } else if (component.types.indexOf("country") >= 0) {
29352                 result.country = component.short_name;
29353             }
29354         }
29355         
29356         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29357         result.addressLine2 = "";
29358         return result;
29359     },
29360     
29361     setZoomLevel: function(zoom)
29362     {
29363         this.gMapContext.map.setZoom(zoom);
29364     },
29365     
29366     show: function()
29367     {
29368         if(!this.el){
29369             return;
29370         }
29371         
29372         this.el.show();
29373         
29374         this.resize();
29375         
29376         this.fireEvent('show', this);
29377     },
29378     
29379     hide: function()
29380     {
29381         if(!this.el){
29382             return;
29383         }
29384         
29385         this.el.hide();
29386         
29387         this.fireEvent('hide', this);
29388     }
29389     
29390 });
29391
29392 Roo.apply(Roo.bootstrap.LocationPicker, {
29393     
29394     OverlayView : function(map, options)
29395     {
29396         options = options || {};
29397         
29398         this.setMap(map);
29399     }
29400     
29401     
29402 });/**
29403  * @class Roo.bootstrap.Alert
29404  * @extends Roo.bootstrap.Component
29405  * Bootstrap Alert class - shows an alert area box
29406  * eg
29407  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29408   Enter a valid email address
29409 </div>
29410  * @licence LGPL
29411  * @cfg {String} title The title of alert
29412  * @cfg {String} html The content of alert
29413  * @cfg {String} weight (  success | info | warning | danger )
29414  * @cfg {String} faicon font-awesomeicon
29415  * 
29416  * @constructor
29417  * Create a new alert
29418  * @param {Object} config The config object
29419  */
29420
29421
29422 Roo.bootstrap.Alert = function(config){
29423     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29424     
29425 };
29426
29427 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29428     
29429     title: '',
29430     html: '',
29431     weight: false,
29432     faicon: false,
29433     
29434     getAutoCreate : function()
29435     {
29436         
29437         var cfg = {
29438             tag : 'div',
29439             cls : 'alert',
29440             cn : [
29441                 {
29442                     tag : 'i',
29443                     cls : 'roo-alert-icon'
29444                     
29445                 },
29446                 {
29447                     tag : 'b',
29448                     cls : 'roo-alert-title',
29449                     html : this.title
29450                 },
29451                 {
29452                     tag : 'span',
29453                     cls : 'roo-alert-text',
29454                     html : this.html
29455                 }
29456             ]
29457         };
29458         
29459         if(this.faicon){
29460             cfg.cn[0].cls += ' fa ' + this.faicon;
29461         }
29462         
29463         if(this.weight){
29464             cfg.cls += ' alert-' + this.weight;
29465         }
29466         
29467         return cfg;
29468     },
29469     
29470     initEvents: function() 
29471     {
29472         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29473     },
29474     
29475     setTitle : function(str)
29476     {
29477         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29478     },
29479     
29480     setText : function(str)
29481     {
29482         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29483     },
29484     
29485     setWeight : function(weight)
29486     {
29487         if(this.weight){
29488             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29489         }
29490         
29491         this.weight = weight;
29492         
29493         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29494     },
29495     
29496     setIcon : function(icon)
29497     {
29498         if(this.faicon){
29499             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29500         }
29501         
29502         this.faicon = icon;
29503         
29504         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29505     },
29506     
29507     hide: function() 
29508     {
29509         this.el.hide();   
29510     },
29511     
29512     show: function() 
29513     {  
29514         this.el.show();   
29515     }
29516     
29517 });
29518
29519  
29520 /*
29521 * Licence: LGPL
29522 */
29523
29524 /**
29525  * @class Roo.bootstrap.UploadCropbox
29526  * @extends Roo.bootstrap.Component
29527  * Bootstrap UploadCropbox class
29528  * @cfg {String} emptyText show when image has been loaded
29529  * @cfg {String} rotateNotify show when image too small to rotate
29530  * @cfg {Number} errorTimeout default 3000
29531  * @cfg {Number} minWidth default 300
29532  * @cfg {Number} minHeight default 300
29533  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29534  * @cfg {Boolean} isDocument (true|false) default false
29535  * @cfg {String} url action url
29536  * @cfg {String} paramName default 'imageUpload'
29537  * @cfg {String} method default POST
29538  * @cfg {Boolean} loadMask (true|false) default true
29539  * @cfg {Boolean} loadingText default 'Loading...'
29540  * 
29541  * @constructor
29542  * Create a new UploadCropbox
29543  * @param {Object} config The config object
29544  */
29545
29546 Roo.bootstrap.UploadCropbox = function(config){
29547     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29548     
29549     this.addEvents({
29550         /**
29551          * @event beforeselectfile
29552          * Fire before select file
29553          * @param {Roo.bootstrap.UploadCropbox} this
29554          */
29555         "beforeselectfile" : true,
29556         /**
29557          * @event initial
29558          * Fire after initEvent
29559          * @param {Roo.bootstrap.UploadCropbox} this
29560          */
29561         "initial" : true,
29562         /**
29563          * @event crop
29564          * Fire after initEvent
29565          * @param {Roo.bootstrap.UploadCropbox} this
29566          * @param {String} data
29567          */
29568         "crop" : true,
29569         /**
29570          * @event prepare
29571          * Fire when preparing the file data
29572          * @param {Roo.bootstrap.UploadCropbox} this
29573          * @param {Object} file
29574          */
29575         "prepare" : true,
29576         /**
29577          * @event exception
29578          * Fire when get exception
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          * @param {XMLHttpRequest} xhr
29581          */
29582         "exception" : true,
29583         /**
29584          * @event beforeloadcanvas
29585          * Fire before load the canvas
29586          * @param {Roo.bootstrap.UploadCropbox} this
29587          * @param {String} src
29588          */
29589         "beforeloadcanvas" : true,
29590         /**
29591          * @event trash
29592          * Fire when trash image
29593          * @param {Roo.bootstrap.UploadCropbox} this
29594          */
29595         "trash" : true,
29596         /**
29597          * @event download
29598          * Fire when download the image
29599          * @param {Roo.bootstrap.UploadCropbox} this
29600          */
29601         "download" : true,
29602         /**
29603          * @event footerbuttonclick
29604          * Fire when footerbuttonclick
29605          * @param {Roo.bootstrap.UploadCropbox} this
29606          * @param {String} type
29607          */
29608         "footerbuttonclick" : true,
29609         /**
29610          * @event resize
29611          * Fire when resize
29612          * @param {Roo.bootstrap.UploadCropbox} this
29613          */
29614         "resize" : true,
29615         /**
29616          * @event rotate
29617          * Fire when rotate the image
29618          * @param {Roo.bootstrap.UploadCropbox} this
29619          * @param {String} pos
29620          */
29621         "rotate" : true,
29622         /**
29623          * @event inspect
29624          * Fire when inspect the file
29625          * @param {Roo.bootstrap.UploadCropbox} this
29626          * @param {Object} file
29627          */
29628         "inspect" : true,
29629         /**
29630          * @event upload
29631          * Fire when xhr upload the file
29632          * @param {Roo.bootstrap.UploadCropbox} this
29633          * @param {Object} data
29634          */
29635         "upload" : true,
29636         /**
29637          * @event arrange
29638          * Fire when arrange the file data
29639          * @param {Roo.bootstrap.UploadCropbox} this
29640          * @param {Object} formData
29641          */
29642         "arrange" : true
29643     });
29644     
29645     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29646 };
29647
29648 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29649     
29650     emptyText : 'Click to upload image',
29651     rotateNotify : 'Image is too small to rotate',
29652     errorTimeout : 3000,
29653     scale : 0,
29654     baseScale : 1,
29655     rotate : 0,
29656     dragable : false,
29657     pinching : false,
29658     mouseX : 0,
29659     mouseY : 0,
29660     cropData : false,
29661     minWidth : 300,
29662     minHeight : 300,
29663     file : false,
29664     exif : {},
29665     baseRotate : 1,
29666     cropType : 'image/jpeg',
29667     buttons : false,
29668     canvasLoaded : false,
29669     isDocument : false,
29670     method : 'POST',
29671     paramName : 'imageUpload',
29672     loadMask : true,
29673     loadingText : 'Loading...',
29674     maskEl : false,
29675     
29676     getAutoCreate : function()
29677     {
29678         var cfg = {
29679             tag : 'div',
29680             cls : 'roo-upload-cropbox',
29681             cn : [
29682                 {
29683                     tag : 'input',
29684                     cls : 'roo-upload-cropbox-selector',
29685                     type : 'file'
29686                 },
29687                 {
29688                     tag : 'div',
29689                     cls : 'roo-upload-cropbox-body',
29690                     style : 'cursor:pointer',
29691                     cn : [
29692                         {
29693                             tag : 'div',
29694                             cls : 'roo-upload-cropbox-preview'
29695                         },
29696                         {
29697                             tag : 'div',
29698                             cls : 'roo-upload-cropbox-thumb'
29699                         },
29700                         {
29701                             tag : 'div',
29702                             cls : 'roo-upload-cropbox-empty-notify',
29703                             html : this.emptyText
29704                         },
29705                         {
29706                             tag : 'div',
29707                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29708                             html : this.rotateNotify
29709                         }
29710                     ]
29711                 },
29712                 {
29713                     tag : 'div',
29714                     cls : 'roo-upload-cropbox-footer',
29715                     cn : {
29716                         tag : 'div',
29717                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29718                         cn : []
29719                     }
29720                 }
29721             ]
29722         };
29723         
29724         return cfg;
29725     },
29726     
29727     onRender : function(ct, position)
29728     {
29729         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29730         
29731         if (this.buttons.length) {
29732             
29733             Roo.each(this.buttons, function(bb) {
29734                 
29735                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29736                 
29737                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29738                 
29739             }, this);
29740         }
29741         
29742         if(this.loadMask){
29743             this.maskEl = this.el;
29744         }
29745     },
29746     
29747     initEvents : function()
29748     {
29749         this.urlAPI = (window.createObjectURL && window) || 
29750                                 (window.URL && URL.revokeObjectURL && URL) || 
29751                                 (window.webkitURL && webkitURL);
29752                         
29753         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29754         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755         
29756         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29757         this.selectorEl.hide();
29758         
29759         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29760         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29761         
29762         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29763         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29764         this.thumbEl.hide();
29765         
29766         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29767         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29768         
29769         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29770         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29771         this.errorEl.hide();
29772         
29773         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29774         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29775         this.footerEl.hide();
29776         
29777         this.setThumbBoxSize();
29778         
29779         this.bind();
29780         
29781         this.resize();
29782         
29783         this.fireEvent('initial', this);
29784     },
29785
29786     bind : function()
29787     {
29788         var _this = this;
29789         
29790         window.addEventListener("resize", function() { _this.resize(); } );
29791         
29792         this.bodyEl.on('click', this.beforeSelectFile, this);
29793         
29794         if(Roo.isTouch){
29795             this.bodyEl.on('touchstart', this.onTouchStart, this);
29796             this.bodyEl.on('touchmove', this.onTouchMove, this);
29797             this.bodyEl.on('touchend', this.onTouchEnd, this);
29798         }
29799         
29800         if(!Roo.isTouch){
29801             this.bodyEl.on('mousedown', this.onMouseDown, this);
29802             this.bodyEl.on('mousemove', this.onMouseMove, this);
29803             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29804             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29805             Roo.get(document).on('mouseup', this.onMouseUp, this);
29806         }
29807         
29808         this.selectorEl.on('change', this.onFileSelected, this);
29809     },
29810     
29811     reset : function()
29812     {    
29813         this.scale = 0;
29814         this.baseScale = 1;
29815         this.rotate = 0;
29816         this.baseRotate = 1;
29817         this.dragable = false;
29818         this.pinching = false;
29819         this.mouseX = 0;
29820         this.mouseY = 0;
29821         this.cropData = false;
29822         this.notifyEl.dom.innerHTML = this.emptyText;
29823         
29824         this.selectorEl.dom.value = '';
29825         
29826     },
29827     
29828     resize : function()
29829     {
29830         if(this.fireEvent('resize', this) != false){
29831             this.setThumbBoxPosition();
29832             this.setCanvasPosition();
29833         }
29834     },
29835     
29836     onFooterButtonClick : function(e, el, o, type)
29837     {
29838         switch (type) {
29839             case 'rotate-left' :
29840                 this.onRotateLeft(e);
29841                 break;
29842             case 'rotate-right' :
29843                 this.onRotateRight(e);
29844                 break;
29845             case 'picture' :
29846                 this.beforeSelectFile(e);
29847                 break;
29848             case 'trash' :
29849                 this.trash(e);
29850                 break;
29851             case 'crop' :
29852                 this.crop(e);
29853                 break;
29854             case 'download' :
29855                 this.download(e);
29856                 break;
29857             default :
29858                 break;
29859         }
29860         
29861         this.fireEvent('footerbuttonclick', this, type);
29862     },
29863     
29864     beforeSelectFile : function(e)
29865     {
29866         e.preventDefault();
29867         
29868         if(this.fireEvent('beforeselectfile', this) != false){
29869             this.selectorEl.dom.click();
29870         }
29871     },
29872     
29873     onFileSelected : function(e)
29874     {
29875         e.preventDefault();
29876         
29877         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29878             return;
29879         }
29880         
29881         var file = this.selectorEl.dom.files[0];
29882         
29883         if(this.fireEvent('inspect', this, file) != false){
29884             this.prepare(file);
29885         }
29886         
29887     },
29888     
29889     trash : function(e)
29890     {
29891         this.fireEvent('trash', this);
29892     },
29893     
29894     download : function(e)
29895     {
29896         this.fireEvent('download', this);
29897     },
29898     
29899     loadCanvas : function(src)
29900     {   
29901         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29902             
29903             this.reset();
29904             
29905             this.imageEl = document.createElement('img');
29906             
29907             var _this = this;
29908             
29909             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29910             
29911             this.imageEl.src = src;
29912         }
29913     },
29914     
29915     onLoadCanvas : function()
29916     {   
29917         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29918         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29919         
29920         this.bodyEl.un('click', this.beforeSelectFile, this);
29921         
29922         this.notifyEl.hide();
29923         this.thumbEl.show();
29924         this.footerEl.show();
29925         
29926         this.baseRotateLevel();
29927         
29928         if(this.isDocument){
29929             this.setThumbBoxSize();
29930         }
29931         
29932         this.setThumbBoxPosition();
29933         
29934         this.baseScaleLevel();
29935         
29936         this.draw();
29937         
29938         this.resize();
29939         
29940         this.canvasLoaded = true;
29941         
29942         if(this.loadMask){
29943             this.maskEl.unmask();
29944         }
29945         
29946     },
29947     
29948     setCanvasPosition : function()
29949     {   
29950         if(!this.canvasEl){
29951             return;
29952         }
29953         
29954         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29955         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29956         
29957         this.previewEl.setLeft(pw);
29958         this.previewEl.setTop(ph);
29959         
29960     },
29961     
29962     onMouseDown : function(e)
29963     {   
29964         e.stopEvent();
29965         
29966         this.dragable = true;
29967         this.pinching = false;
29968         
29969         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29970             this.dragable = false;
29971             return;
29972         }
29973         
29974         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29975         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29976         
29977     },
29978     
29979     onMouseMove : function(e)
29980     {   
29981         e.stopEvent();
29982         
29983         if(!this.canvasLoaded){
29984             return;
29985         }
29986         
29987         if (!this.dragable){
29988             return;
29989         }
29990         
29991         var minX = Math.ceil(this.thumbEl.getLeft(true));
29992         var minY = Math.ceil(this.thumbEl.getTop(true));
29993         
29994         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29995         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29996         
29997         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29998         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29999         
30000         x = x - this.mouseX;
30001         y = y - this.mouseY;
30002         
30003         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30004         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30005         
30006         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30007         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30008         
30009         this.previewEl.setLeft(bgX);
30010         this.previewEl.setTop(bgY);
30011         
30012         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30013         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30014     },
30015     
30016     onMouseUp : function(e)
30017     {   
30018         e.stopEvent();
30019         
30020         this.dragable = false;
30021     },
30022     
30023     onMouseWheel : function(e)
30024     {   
30025         e.stopEvent();
30026         
30027         this.startScale = this.scale;
30028         
30029         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30030         
30031         if(!this.zoomable()){
30032             this.scale = this.startScale;
30033             return;
30034         }
30035         
30036         this.draw();
30037         
30038         return;
30039     },
30040     
30041     zoomable : function()
30042     {
30043         var minScale = this.thumbEl.getWidth() / this.minWidth;
30044         
30045         if(this.minWidth < this.minHeight){
30046             minScale = this.thumbEl.getHeight() / this.minHeight;
30047         }
30048         
30049         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30050         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30051         
30052         if(
30053                 this.isDocument &&
30054                 (this.rotate == 0 || this.rotate == 180) && 
30055                 (
30056                     width > this.imageEl.OriginWidth || 
30057                     height > this.imageEl.OriginHeight ||
30058                     (width < this.minWidth && height < this.minHeight)
30059                 )
30060         ){
30061             return false;
30062         }
30063         
30064         if(
30065                 this.isDocument &&
30066                 (this.rotate == 90 || this.rotate == 270) && 
30067                 (
30068                     width > this.imageEl.OriginWidth || 
30069                     height > this.imageEl.OriginHeight ||
30070                     (width < this.minHeight && height < this.minWidth)
30071                 )
30072         ){
30073             return false;
30074         }
30075         
30076         if(
30077                 !this.isDocument &&
30078                 (this.rotate == 0 || this.rotate == 180) && 
30079                 (
30080                     width < this.minWidth || 
30081                     width > this.imageEl.OriginWidth || 
30082                     height < this.minHeight || 
30083                     height > this.imageEl.OriginHeight
30084                 )
30085         ){
30086             return false;
30087         }
30088         
30089         if(
30090                 !this.isDocument &&
30091                 (this.rotate == 90 || this.rotate == 270) && 
30092                 (
30093                     width < this.minHeight || 
30094                     width > this.imageEl.OriginWidth || 
30095                     height < this.minWidth || 
30096                     height > this.imageEl.OriginHeight
30097                 )
30098         ){
30099             return false;
30100         }
30101         
30102         return true;
30103         
30104     },
30105     
30106     onRotateLeft : function(e)
30107     {   
30108         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30109             
30110             var minScale = this.thumbEl.getWidth() / this.minWidth;
30111             
30112             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30113             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30114             
30115             this.startScale = this.scale;
30116             
30117             while (this.getScaleLevel() < minScale){
30118             
30119                 this.scale = this.scale + 1;
30120                 
30121                 if(!this.zoomable()){
30122                     break;
30123                 }
30124                 
30125                 if(
30126                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30127                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30128                 ){
30129                     continue;
30130                 }
30131                 
30132                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30133
30134                 this.draw();
30135                 
30136                 return;
30137             }
30138             
30139             this.scale = this.startScale;
30140             
30141             this.onRotateFail();
30142             
30143             return false;
30144         }
30145         
30146         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30147
30148         if(this.isDocument){
30149             this.setThumbBoxSize();
30150             this.setThumbBoxPosition();
30151             this.setCanvasPosition();
30152         }
30153         
30154         this.draw();
30155         
30156         this.fireEvent('rotate', this, 'left');
30157         
30158     },
30159     
30160     onRotateRight : function(e)
30161     {
30162         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30163             
30164             var minScale = this.thumbEl.getWidth() / this.minWidth;
30165         
30166             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30167             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30168             
30169             this.startScale = this.scale;
30170             
30171             while (this.getScaleLevel() < minScale){
30172             
30173                 this.scale = this.scale + 1;
30174                 
30175                 if(!this.zoomable()){
30176                     break;
30177                 }
30178                 
30179                 if(
30180                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30181                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30182                 ){
30183                     continue;
30184                 }
30185                 
30186                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30187
30188                 this.draw();
30189                 
30190                 return;
30191             }
30192             
30193             this.scale = this.startScale;
30194             
30195             this.onRotateFail();
30196             
30197             return false;
30198         }
30199         
30200         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30201
30202         if(this.isDocument){
30203             this.setThumbBoxSize();
30204             this.setThumbBoxPosition();
30205             this.setCanvasPosition();
30206         }
30207         
30208         this.draw();
30209         
30210         this.fireEvent('rotate', this, 'right');
30211     },
30212     
30213     onRotateFail : function()
30214     {
30215         this.errorEl.show(true);
30216         
30217         var _this = this;
30218         
30219         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30220     },
30221     
30222     draw : function()
30223     {
30224         this.previewEl.dom.innerHTML = '';
30225         
30226         var canvasEl = document.createElement("canvas");
30227         
30228         var contextEl = canvasEl.getContext("2d");
30229         
30230         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30231         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30232         var center = this.imageEl.OriginWidth / 2;
30233         
30234         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30235             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30236             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30237             center = this.imageEl.OriginHeight / 2;
30238         }
30239         
30240         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30241         
30242         contextEl.translate(center, center);
30243         contextEl.rotate(this.rotate * Math.PI / 180);
30244
30245         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30246         
30247         this.canvasEl = document.createElement("canvas");
30248         
30249         this.contextEl = this.canvasEl.getContext("2d");
30250         
30251         switch (this.rotate) {
30252             case 0 :
30253                 
30254                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30255                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30256                 
30257                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30258                 
30259                 break;
30260             case 90 : 
30261                 
30262                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30263                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30264                 
30265                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30266                     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);
30267                     break;
30268                 }
30269                 
30270                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30271                 
30272                 break;
30273             case 180 :
30274                 
30275                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30276                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30277                 
30278                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30279                     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);
30280                     break;
30281                 }
30282                 
30283                 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);
30284                 
30285                 break;
30286             case 270 :
30287                 
30288                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30289                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30290         
30291                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30292                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30293                     break;
30294                 }
30295                 
30296                 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);
30297                 
30298                 break;
30299             default : 
30300                 break;
30301         }
30302         
30303         this.previewEl.appendChild(this.canvasEl);
30304         
30305         this.setCanvasPosition();
30306     },
30307     
30308     crop : function()
30309     {
30310         if(!this.canvasLoaded){
30311             return;
30312         }
30313         
30314         var imageCanvas = document.createElement("canvas");
30315         
30316         var imageContext = imageCanvas.getContext("2d");
30317         
30318         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30319         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30320         
30321         var center = imageCanvas.width / 2;
30322         
30323         imageContext.translate(center, center);
30324         
30325         imageContext.rotate(this.rotate * Math.PI / 180);
30326         
30327         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30328         
30329         var canvas = document.createElement("canvas");
30330         
30331         var context = canvas.getContext("2d");
30332                 
30333         canvas.width = this.minWidth;
30334         canvas.height = this.minHeight;
30335
30336         switch (this.rotate) {
30337             case 0 :
30338                 
30339                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30340                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30341                 
30342                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30343                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30344                 
30345                 var targetWidth = this.minWidth - 2 * x;
30346                 var targetHeight = this.minHeight - 2 * y;
30347                 
30348                 var scale = 1;
30349                 
30350                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30351                     scale = targetWidth / width;
30352                 }
30353                 
30354                 if(x > 0 && y == 0){
30355                     scale = targetHeight / height;
30356                 }
30357                 
30358                 if(x > 0 && y > 0){
30359                     scale = targetWidth / width;
30360                     
30361                     if(width < height){
30362                         scale = targetHeight / height;
30363                     }
30364                 }
30365                 
30366                 context.scale(scale, scale);
30367                 
30368                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30369                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30370
30371                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30372                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30373
30374                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30375                 
30376                 break;
30377             case 90 : 
30378                 
30379                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30380                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30381                 
30382                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30383                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30384                 
30385                 var targetWidth = this.minWidth - 2 * x;
30386                 var targetHeight = this.minHeight - 2 * y;
30387                 
30388                 var scale = 1;
30389                 
30390                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30391                     scale = targetWidth / width;
30392                 }
30393                 
30394                 if(x > 0 && y == 0){
30395                     scale = targetHeight / height;
30396                 }
30397                 
30398                 if(x > 0 && y > 0){
30399                     scale = targetWidth / width;
30400                     
30401                     if(width < height){
30402                         scale = targetHeight / height;
30403                     }
30404                 }
30405                 
30406                 context.scale(scale, scale);
30407                 
30408                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30409                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30410
30411                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30412                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30413                 
30414                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30415                 
30416                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30417                 
30418                 break;
30419             case 180 :
30420                 
30421                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30422                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30423                 
30424                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30425                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30426                 
30427                 var targetWidth = this.minWidth - 2 * x;
30428                 var targetHeight = this.minHeight - 2 * y;
30429                 
30430                 var scale = 1;
30431                 
30432                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30433                     scale = targetWidth / width;
30434                 }
30435                 
30436                 if(x > 0 && y == 0){
30437                     scale = targetHeight / height;
30438                 }
30439                 
30440                 if(x > 0 && y > 0){
30441                     scale = targetWidth / width;
30442                     
30443                     if(width < height){
30444                         scale = targetHeight / height;
30445                     }
30446                 }
30447                 
30448                 context.scale(scale, scale);
30449                 
30450                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30451                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30452
30453                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30454                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30455
30456                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30457                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30458                 
30459                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30460                 
30461                 break;
30462             case 270 :
30463                 
30464                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30465                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30466                 
30467                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30468                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30469                 
30470                 var targetWidth = this.minWidth - 2 * x;
30471                 var targetHeight = this.minHeight - 2 * y;
30472                 
30473                 var scale = 1;
30474                 
30475                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30476                     scale = targetWidth / width;
30477                 }
30478                 
30479                 if(x > 0 && y == 0){
30480                     scale = targetHeight / height;
30481                 }
30482                 
30483                 if(x > 0 && y > 0){
30484                     scale = targetWidth / width;
30485                     
30486                     if(width < height){
30487                         scale = targetHeight / height;
30488                     }
30489                 }
30490                 
30491                 context.scale(scale, scale);
30492                 
30493                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30494                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30495
30496                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30497                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30498                 
30499                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30500                 
30501                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30502                 
30503                 break;
30504             default : 
30505                 break;
30506         }
30507         
30508         this.cropData = canvas.toDataURL(this.cropType);
30509         
30510         if(this.fireEvent('crop', this, this.cropData) !== false){
30511             this.process(this.file, this.cropData);
30512         }
30513         
30514         return;
30515         
30516     },
30517     
30518     setThumbBoxSize : function()
30519     {
30520         var width, height;
30521         
30522         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30523             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30524             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30525             
30526             this.minWidth = width;
30527             this.minHeight = height;
30528             
30529             if(this.rotate == 90 || this.rotate == 270){
30530                 this.minWidth = height;
30531                 this.minHeight = width;
30532             }
30533         }
30534         
30535         height = 300;
30536         width = Math.ceil(this.minWidth * height / this.minHeight);
30537         
30538         if(this.minWidth > this.minHeight){
30539             width = 300;
30540             height = Math.ceil(this.minHeight * width / this.minWidth);
30541         }
30542         
30543         this.thumbEl.setStyle({
30544             width : width + 'px',
30545             height : height + 'px'
30546         });
30547
30548         return;
30549             
30550     },
30551     
30552     setThumbBoxPosition : function()
30553     {
30554         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30555         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30556         
30557         this.thumbEl.setLeft(x);
30558         this.thumbEl.setTop(y);
30559         
30560     },
30561     
30562     baseRotateLevel : function()
30563     {
30564         this.baseRotate = 1;
30565         
30566         if(
30567                 typeof(this.exif) != 'undefined' &&
30568                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30569                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30570         ){
30571             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30572         }
30573         
30574         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30575         
30576     },
30577     
30578     baseScaleLevel : function()
30579     {
30580         var width, height;
30581         
30582         if(this.isDocument){
30583             
30584             if(this.baseRotate == 6 || this.baseRotate == 8){
30585             
30586                 height = this.thumbEl.getHeight();
30587                 this.baseScale = height / this.imageEl.OriginWidth;
30588
30589                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30590                     width = this.thumbEl.getWidth();
30591                     this.baseScale = width / this.imageEl.OriginHeight;
30592                 }
30593
30594                 return;
30595             }
30596
30597             height = this.thumbEl.getHeight();
30598             this.baseScale = height / this.imageEl.OriginHeight;
30599
30600             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30601                 width = this.thumbEl.getWidth();
30602                 this.baseScale = width / this.imageEl.OriginWidth;
30603             }
30604
30605             return;
30606         }
30607         
30608         if(this.baseRotate == 6 || this.baseRotate == 8){
30609             
30610             width = this.thumbEl.getHeight();
30611             this.baseScale = width / this.imageEl.OriginHeight;
30612             
30613             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30614                 height = this.thumbEl.getWidth();
30615                 this.baseScale = height / this.imageEl.OriginHeight;
30616             }
30617             
30618             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30619                 height = this.thumbEl.getWidth();
30620                 this.baseScale = height / this.imageEl.OriginHeight;
30621                 
30622                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30623                     width = this.thumbEl.getHeight();
30624                     this.baseScale = width / this.imageEl.OriginWidth;
30625                 }
30626             }
30627             
30628             return;
30629         }
30630         
30631         width = this.thumbEl.getWidth();
30632         this.baseScale = width / this.imageEl.OriginWidth;
30633         
30634         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30635             height = this.thumbEl.getHeight();
30636             this.baseScale = height / this.imageEl.OriginHeight;
30637         }
30638         
30639         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30640             
30641             height = this.thumbEl.getHeight();
30642             this.baseScale = height / this.imageEl.OriginHeight;
30643             
30644             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30645                 width = this.thumbEl.getWidth();
30646                 this.baseScale = width / this.imageEl.OriginWidth;
30647             }
30648             
30649         }
30650         
30651         return;
30652     },
30653     
30654     getScaleLevel : function()
30655     {
30656         return this.baseScale * Math.pow(1.1, this.scale);
30657     },
30658     
30659     onTouchStart : function(e)
30660     {
30661         if(!this.canvasLoaded){
30662             this.beforeSelectFile(e);
30663             return;
30664         }
30665         
30666         var touches = e.browserEvent.touches;
30667         
30668         if(!touches){
30669             return;
30670         }
30671         
30672         if(touches.length == 1){
30673             this.onMouseDown(e);
30674             return;
30675         }
30676         
30677         if(touches.length != 2){
30678             return;
30679         }
30680         
30681         var coords = [];
30682         
30683         for(var i = 0, finger; finger = touches[i]; i++){
30684             coords.push(finger.pageX, finger.pageY);
30685         }
30686         
30687         var x = Math.pow(coords[0] - coords[2], 2);
30688         var y = Math.pow(coords[1] - coords[3], 2);
30689         
30690         this.startDistance = Math.sqrt(x + y);
30691         
30692         this.startScale = this.scale;
30693         
30694         this.pinching = true;
30695         this.dragable = false;
30696         
30697     },
30698     
30699     onTouchMove : function(e)
30700     {
30701         if(!this.pinching && !this.dragable){
30702             return;
30703         }
30704         
30705         var touches = e.browserEvent.touches;
30706         
30707         if(!touches){
30708             return;
30709         }
30710         
30711         if(this.dragable){
30712             this.onMouseMove(e);
30713             return;
30714         }
30715         
30716         var coords = [];
30717         
30718         for(var i = 0, finger; finger = touches[i]; i++){
30719             coords.push(finger.pageX, finger.pageY);
30720         }
30721         
30722         var x = Math.pow(coords[0] - coords[2], 2);
30723         var y = Math.pow(coords[1] - coords[3], 2);
30724         
30725         this.endDistance = Math.sqrt(x + y);
30726         
30727         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30728         
30729         if(!this.zoomable()){
30730             this.scale = this.startScale;
30731             return;
30732         }
30733         
30734         this.draw();
30735         
30736     },
30737     
30738     onTouchEnd : function(e)
30739     {
30740         this.pinching = false;
30741         this.dragable = false;
30742         
30743     },
30744     
30745     process : function(file, crop)
30746     {
30747         if(this.loadMask){
30748             this.maskEl.mask(this.loadingText);
30749         }
30750         
30751         this.xhr = new XMLHttpRequest();
30752         
30753         file.xhr = this.xhr;
30754
30755         this.xhr.open(this.method, this.url, true);
30756         
30757         var headers = {
30758             "Accept": "application/json",
30759             "Cache-Control": "no-cache",
30760             "X-Requested-With": "XMLHttpRequest"
30761         };
30762         
30763         for (var headerName in headers) {
30764             var headerValue = headers[headerName];
30765             if (headerValue) {
30766                 this.xhr.setRequestHeader(headerName, headerValue);
30767             }
30768         }
30769         
30770         var _this = this;
30771         
30772         this.xhr.onload = function()
30773         {
30774             _this.xhrOnLoad(_this.xhr);
30775         }
30776         
30777         this.xhr.onerror = function()
30778         {
30779             _this.xhrOnError(_this.xhr);
30780         }
30781         
30782         var formData = new FormData();
30783
30784         formData.append('returnHTML', 'NO');
30785         
30786         if(crop){
30787             formData.append('crop', crop);
30788         }
30789         
30790         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30791             formData.append(this.paramName, file, file.name);
30792         }
30793         
30794         if(typeof(file.filename) != 'undefined'){
30795             formData.append('filename', file.filename);
30796         }
30797         
30798         if(typeof(file.mimetype) != 'undefined'){
30799             formData.append('mimetype', file.mimetype);
30800         }
30801         
30802         if(this.fireEvent('arrange', this, formData) != false){
30803             this.xhr.send(formData);
30804         };
30805     },
30806     
30807     xhrOnLoad : function(xhr)
30808     {
30809         if(this.loadMask){
30810             this.maskEl.unmask();
30811         }
30812         
30813         if (xhr.readyState !== 4) {
30814             this.fireEvent('exception', this, xhr);
30815             return;
30816         }
30817
30818         var response = Roo.decode(xhr.responseText);
30819         
30820         if(!response.success){
30821             this.fireEvent('exception', this, xhr);
30822             return;
30823         }
30824         
30825         var response = Roo.decode(xhr.responseText);
30826         
30827         this.fireEvent('upload', this, response);
30828         
30829     },
30830     
30831     xhrOnError : function()
30832     {
30833         if(this.loadMask){
30834             this.maskEl.unmask();
30835         }
30836         
30837         Roo.log('xhr on error');
30838         
30839         var response = Roo.decode(xhr.responseText);
30840           
30841         Roo.log(response);
30842         
30843     },
30844     
30845     prepare : function(file)
30846     {   
30847         if(this.loadMask){
30848             this.maskEl.mask(this.loadingText);
30849         }
30850         
30851         this.file = false;
30852         this.exif = {};
30853         
30854         if(typeof(file) === 'string'){
30855             this.loadCanvas(file);
30856             return;
30857         }
30858         
30859         if(!file || !this.urlAPI){
30860             return;
30861         }
30862         
30863         this.file = file;
30864         this.cropType = file.type;
30865         
30866         var _this = this;
30867         
30868         if(this.fireEvent('prepare', this, this.file) != false){
30869             
30870             var reader = new FileReader();
30871             
30872             reader.onload = function (e) {
30873                 if (e.target.error) {
30874                     Roo.log(e.target.error);
30875                     return;
30876                 }
30877                 
30878                 var buffer = e.target.result,
30879                     dataView = new DataView(buffer),
30880                     offset = 2,
30881                     maxOffset = dataView.byteLength - 4,
30882                     markerBytes,
30883                     markerLength;
30884                 
30885                 if (dataView.getUint16(0) === 0xffd8) {
30886                     while (offset < maxOffset) {
30887                         markerBytes = dataView.getUint16(offset);
30888                         
30889                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30890                             markerLength = dataView.getUint16(offset + 2) + 2;
30891                             if (offset + markerLength > dataView.byteLength) {
30892                                 Roo.log('Invalid meta data: Invalid segment size.');
30893                                 break;
30894                             }
30895                             
30896                             if(markerBytes == 0xffe1){
30897                                 _this.parseExifData(
30898                                     dataView,
30899                                     offset,
30900                                     markerLength
30901                                 );
30902                             }
30903                             
30904                             offset += markerLength;
30905                             
30906                             continue;
30907                         }
30908                         
30909                         break;
30910                     }
30911                     
30912                 }
30913                 
30914                 var url = _this.urlAPI.createObjectURL(_this.file);
30915                 
30916                 _this.loadCanvas(url);
30917                 
30918                 return;
30919             }
30920             
30921             reader.readAsArrayBuffer(this.file);
30922             
30923         }
30924         
30925     },
30926     
30927     parseExifData : function(dataView, offset, length)
30928     {
30929         var tiffOffset = offset + 10,
30930             littleEndian,
30931             dirOffset;
30932     
30933         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30934             // No Exif data, might be XMP data instead
30935             return;
30936         }
30937         
30938         // Check for the ASCII code for "Exif" (0x45786966):
30939         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30940             // No Exif data, might be XMP data instead
30941             return;
30942         }
30943         if (tiffOffset + 8 > dataView.byteLength) {
30944             Roo.log('Invalid Exif data: Invalid segment size.');
30945             return;
30946         }
30947         // Check for the two null bytes:
30948         if (dataView.getUint16(offset + 8) !== 0x0000) {
30949             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30950             return;
30951         }
30952         // Check the byte alignment:
30953         switch (dataView.getUint16(tiffOffset)) {
30954         case 0x4949:
30955             littleEndian = true;
30956             break;
30957         case 0x4D4D:
30958             littleEndian = false;
30959             break;
30960         default:
30961             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30962             return;
30963         }
30964         // Check for the TIFF tag marker (0x002A):
30965         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30966             Roo.log('Invalid Exif data: Missing TIFF marker.');
30967             return;
30968         }
30969         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30970         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30971         
30972         this.parseExifTags(
30973             dataView,
30974             tiffOffset,
30975             tiffOffset + dirOffset,
30976             littleEndian
30977         );
30978     },
30979     
30980     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30981     {
30982         var tagsNumber,
30983             dirEndOffset,
30984             i;
30985         if (dirOffset + 6 > dataView.byteLength) {
30986             Roo.log('Invalid Exif data: Invalid directory offset.');
30987             return;
30988         }
30989         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30990         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30991         if (dirEndOffset + 4 > dataView.byteLength) {
30992             Roo.log('Invalid Exif data: Invalid directory size.');
30993             return;
30994         }
30995         for (i = 0; i < tagsNumber; i += 1) {
30996             this.parseExifTag(
30997                 dataView,
30998                 tiffOffset,
30999                 dirOffset + 2 + 12 * i, // tag offset
31000                 littleEndian
31001             );
31002         }
31003         // Return the offset to the next directory:
31004         return dataView.getUint32(dirEndOffset, littleEndian);
31005     },
31006     
31007     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31008     {
31009         var tag = dataView.getUint16(offset, littleEndian);
31010         
31011         this.exif[tag] = this.getExifValue(
31012             dataView,
31013             tiffOffset,
31014             offset,
31015             dataView.getUint16(offset + 2, littleEndian), // tag type
31016             dataView.getUint32(offset + 4, littleEndian), // tag length
31017             littleEndian
31018         );
31019     },
31020     
31021     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31022     {
31023         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31024             tagSize,
31025             dataOffset,
31026             values,
31027             i,
31028             str,
31029             c;
31030     
31031         if (!tagType) {
31032             Roo.log('Invalid Exif data: Invalid tag type.');
31033             return;
31034         }
31035         
31036         tagSize = tagType.size * length;
31037         // Determine if the value is contained in the dataOffset bytes,
31038         // or if the value at the dataOffset is a pointer to the actual data:
31039         dataOffset = tagSize > 4 ?
31040                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31041         if (dataOffset + tagSize > dataView.byteLength) {
31042             Roo.log('Invalid Exif data: Invalid data offset.');
31043             return;
31044         }
31045         if (length === 1) {
31046             return tagType.getValue(dataView, dataOffset, littleEndian);
31047         }
31048         values = [];
31049         for (i = 0; i < length; i += 1) {
31050             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31051         }
31052         
31053         if (tagType.ascii) {
31054             str = '';
31055             // Concatenate the chars:
31056             for (i = 0; i < values.length; i += 1) {
31057                 c = values[i];
31058                 // Ignore the terminating NULL byte(s):
31059                 if (c === '\u0000') {
31060                     break;
31061                 }
31062                 str += c;
31063             }
31064             return str;
31065         }
31066         return values;
31067     }
31068     
31069 });
31070
31071 Roo.apply(Roo.bootstrap.UploadCropbox, {
31072     tags : {
31073         'Orientation': 0x0112
31074     },
31075     
31076     Orientation: {
31077             1: 0, //'top-left',
31078 //            2: 'top-right',
31079             3: 180, //'bottom-right',
31080 //            4: 'bottom-left',
31081 //            5: 'left-top',
31082             6: 90, //'right-top',
31083 //            7: 'right-bottom',
31084             8: 270 //'left-bottom'
31085     },
31086     
31087     exifTagTypes : {
31088         // byte, 8-bit unsigned int:
31089         1: {
31090             getValue: function (dataView, dataOffset) {
31091                 return dataView.getUint8(dataOffset);
31092             },
31093             size: 1
31094         },
31095         // ascii, 8-bit byte:
31096         2: {
31097             getValue: function (dataView, dataOffset) {
31098                 return String.fromCharCode(dataView.getUint8(dataOffset));
31099             },
31100             size: 1,
31101             ascii: true
31102         },
31103         // short, 16 bit int:
31104         3: {
31105             getValue: function (dataView, dataOffset, littleEndian) {
31106                 return dataView.getUint16(dataOffset, littleEndian);
31107             },
31108             size: 2
31109         },
31110         // long, 32 bit int:
31111         4: {
31112             getValue: function (dataView, dataOffset, littleEndian) {
31113                 return dataView.getUint32(dataOffset, littleEndian);
31114             },
31115             size: 4
31116         },
31117         // rational = two long values, first is numerator, second is denominator:
31118         5: {
31119             getValue: function (dataView, dataOffset, littleEndian) {
31120                 return dataView.getUint32(dataOffset, littleEndian) /
31121                     dataView.getUint32(dataOffset + 4, littleEndian);
31122             },
31123             size: 8
31124         },
31125         // slong, 32 bit signed int:
31126         9: {
31127             getValue: function (dataView, dataOffset, littleEndian) {
31128                 return dataView.getInt32(dataOffset, littleEndian);
31129             },
31130             size: 4
31131         },
31132         // srational, two slongs, first is numerator, second is denominator:
31133         10: {
31134             getValue: function (dataView, dataOffset, littleEndian) {
31135                 return dataView.getInt32(dataOffset, littleEndian) /
31136                     dataView.getInt32(dataOffset + 4, littleEndian);
31137             },
31138             size: 8
31139         }
31140     },
31141     
31142     footer : {
31143         STANDARD : [
31144             {
31145                 tag : 'div',
31146                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31147                 action : 'rotate-left',
31148                 cn : [
31149                     {
31150                         tag : 'button',
31151                         cls : 'btn btn-default',
31152                         html : '<i class="fa fa-undo"></i>'
31153                     }
31154                 ]
31155             },
31156             {
31157                 tag : 'div',
31158                 cls : 'btn-group roo-upload-cropbox-picture',
31159                 action : 'picture',
31160                 cn : [
31161                     {
31162                         tag : 'button',
31163                         cls : 'btn btn-default',
31164                         html : '<i class="fa fa-picture-o"></i>'
31165                     }
31166                 ]
31167             },
31168             {
31169                 tag : 'div',
31170                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31171                 action : 'rotate-right',
31172                 cn : [
31173                     {
31174                         tag : 'button',
31175                         cls : 'btn btn-default',
31176                         html : '<i class="fa fa-repeat"></i>'
31177                     }
31178                 ]
31179             }
31180         ],
31181         DOCUMENT : [
31182             {
31183                 tag : 'div',
31184                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31185                 action : 'rotate-left',
31186                 cn : [
31187                     {
31188                         tag : 'button',
31189                         cls : 'btn btn-default',
31190                         html : '<i class="fa fa-undo"></i>'
31191                     }
31192                 ]
31193             },
31194             {
31195                 tag : 'div',
31196                 cls : 'btn-group roo-upload-cropbox-download',
31197                 action : 'download',
31198                 cn : [
31199                     {
31200                         tag : 'button',
31201                         cls : 'btn btn-default',
31202                         html : '<i class="fa fa-download"></i>'
31203                     }
31204                 ]
31205             },
31206             {
31207                 tag : 'div',
31208                 cls : 'btn-group roo-upload-cropbox-crop',
31209                 action : 'crop',
31210                 cn : [
31211                     {
31212                         tag : 'button',
31213                         cls : 'btn btn-default',
31214                         html : '<i class="fa fa-crop"></i>'
31215                     }
31216                 ]
31217             },
31218             {
31219                 tag : 'div',
31220                 cls : 'btn-group roo-upload-cropbox-trash',
31221                 action : 'trash',
31222                 cn : [
31223                     {
31224                         tag : 'button',
31225                         cls : 'btn btn-default',
31226                         html : '<i class="fa fa-trash"></i>'
31227                     }
31228                 ]
31229             },
31230             {
31231                 tag : 'div',
31232                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31233                 action : 'rotate-right',
31234                 cn : [
31235                     {
31236                         tag : 'button',
31237                         cls : 'btn btn-default',
31238                         html : '<i class="fa fa-repeat"></i>'
31239                     }
31240                 ]
31241             }
31242         ],
31243         ROTATOR : [
31244             {
31245                 tag : 'div',
31246                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31247                 action : 'rotate-left',
31248                 cn : [
31249                     {
31250                         tag : 'button',
31251                         cls : 'btn btn-default',
31252                         html : '<i class="fa fa-undo"></i>'
31253                     }
31254                 ]
31255             },
31256             {
31257                 tag : 'div',
31258                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31259                 action : 'rotate-right',
31260                 cn : [
31261                     {
31262                         tag : 'button',
31263                         cls : 'btn btn-default',
31264                         html : '<i class="fa fa-repeat"></i>'
31265                     }
31266                 ]
31267             }
31268         ]
31269     }
31270 });
31271
31272 /*
31273 * Licence: LGPL
31274 */
31275
31276 /**
31277  * @class Roo.bootstrap.DocumentManager
31278  * @extends Roo.bootstrap.Component
31279  * Bootstrap DocumentManager class
31280  * @cfg {String} paramName default 'imageUpload'
31281  * @cfg {String} toolTipName default 'filename'
31282  * @cfg {String} method default POST
31283  * @cfg {String} url action url
31284  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31285  * @cfg {Boolean} multiple multiple upload default true
31286  * @cfg {Number} thumbSize default 300
31287  * @cfg {String} fieldLabel
31288  * @cfg {Number} labelWidth default 4
31289  * @cfg {String} labelAlign (left|top) default left
31290  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31291 * @cfg {Number} labellg set the width of label (1-12)
31292  * @cfg {Number} labelmd set the width of label (1-12)
31293  * @cfg {Number} labelsm set the width of label (1-12)
31294  * @cfg {Number} labelxs set the width of label (1-12)
31295  * 
31296  * @constructor
31297  * Create a new DocumentManager
31298  * @param {Object} config The config object
31299  */
31300
31301 Roo.bootstrap.DocumentManager = function(config){
31302     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31303     
31304     this.files = [];
31305     this.delegates = [];
31306     
31307     this.addEvents({
31308         /**
31309          * @event initial
31310          * Fire when initial the DocumentManager
31311          * @param {Roo.bootstrap.DocumentManager} this
31312          */
31313         "initial" : true,
31314         /**
31315          * @event inspect
31316          * inspect selected file
31317          * @param {Roo.bootstrap.DocumentManager} this
31318          * @param {File} file
31319          */
31320         "inspect" : true,
31321         /**
31322          * @event exception
31323          * Fire when xhr load exception
31324          * @param {Roo.bootstrap.DocumentManager} this
31325          * @param {XMLHttpRequest} xhr
31326          */
31327         "exception" : true,
31328         /**
31329          * @event afterupload
31330          * Fire when xhr load exception
31331          * @param {Roo.bootstrap.DocumentManager} this
31332          * @param {XMLHttpRequest} xhr
31333          */
31334         "afterupload" : true,
31335         /**
31336          * @event prepare
31337          * prepare the form data
31338          * @param {Roo.bootstrap.DocumentManager} this
31339          * @param {Object} formData
31340          */
31341         "prepare" : true,
31342         /**
31343          * @event remove
31344          * Fire when remove the file
31345          * @param {Roo.bootstrap.DocumentManager} this
31346          * @param {Object} file
31347          */
31348         "remove" : true,
31349         /**
31350          * @event refresh
31351          * Fire after refresh the file
31352          * @param {Roo.bootstrap.DocumentManager} this
31353          */
31354         "refresh" : true,
31355         /**
31356          * @event click
31357          * Fire after click the image
31358          * @param {Roo.bootstrap.DocumentManager} this
31359          * @param {Object} file
31360          */
31361         "click" : true,
31362         /**
31363          * @event edit
31364          * Fire when upload a image and editable set to true
31365          * @param {Roo.bootstrap.DocumentManager} this
31366          * @param {Object} file
31367          */
31368         "edit" : true,
31369         /**
31370          * @event beforeselectfile
31371          * Fire before select file
31372          * @param {Roo.bootstrap.DocumentManager} this
31373          */
31374         "beforeselectfile" : true,
31375         /**
31376          * @event process
31377          * Fire before process file
31378          * @param {Roo.bootstrap.DocumentManager} this
31379          * @param {Object} file
31380          */
31381         "process" : true,
31382         /**
31383          * @event previewrendered
31384          * Fire when preview rendered
31385          * @param {Roo.bootstrap.DocumentManager} this
31386          * @param {Object} file
31387          */
31388         "previewrendered" : true,
31389         /**
31390          */
31391         "previewResize" : true
31392         
31393     });
31394 };
31395
31396 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31397     
31398     boxes : 0,
31399     inputName : '',
31400     thumbSize : 300,
31401     multiple : true,
31402     files : false,
31403     method : 'POST',
31404     url : '',
31405     paramName : 'imageUpload',
31406     toolTipName : 'filename',
31407     fieldLabel : '',
31408     labelWidth : 4,
31409     labelAlign : 'left',
31410     editable : true,
31411     delegates : false,
31412     xhr : false, 
31413     
31414     labellg : 0,
31415     labelmd : 0,
31416     labelsm : 0,
31417     labelxs : 0,
31418     
31419     getAutoCreate : function()
31420     {   
31421         var managerWidget = {
31422             tag : 'div',
31423             cls : 'roo-document-manager',
31424             cn : [
31425                 {
31426                     tag : 'input',
31427                     cls : 'roo-document-manager-selector',
31428                     type : 'file'
31429                 },
31430                 {
31431                     tag : 'div',
31432                     cls : 'roo-document-manager-uploader',
31433                     cn : [
31434                         {
31435                             tag : 'div',
31436                             cls : 'roo-document-manager-upload-btn',
31437                             html : '<i class="fa fa-plus"></i>'
31438                         }
31439                     ]
31440                     
31441                 }
31442             ]
31443         };
31444         
31445         var content = [
31446             {
31447                 tag : 'div',
31448                 cls : 'column col-md-12',
31449                 cn : managerWidget
31450             }
31451         ];
31452         
31453         if(this.fieldLabel.length){
31454             
31455             content = [
31456                 {
31457                     tag : 'div',
31458                     cls : 'column col-md-12',
31459                     html : this.fieldLabel
31460                 },
31461                 {
31462                     tag : 'div',
31463                     cls : 'column col-md-12',
31464                     cn : managerWidget
31465                 }
31466             ];
31467
31468             if(this.labelAlign == 'left'){
31469                 content = [
31470                     {
31471                         tag : 'div',
31472                         cls : 'column',
31473                         html : this.fieldLabel
31474                     },
31475                     {
31476                         tag : 'div',
31477                         cls : 'column',
31478                         cn : managerWidget
31479                     }
31480                 ];
31481                 
31482                 if(this.labelWidth > 12){
31483                     content[0].style = "width: " + this.labelWidth + 'px';
31484                 }
31485
31486                 if(this.labelWidth < 13 && this.labelmd == 0){
31487                     this.labelmd = this.labelWidth;
31488                 }
31489
31490                 if(this.labellg > 0){
31491                     content[0].cls += ' col-lg-' + this.labellg;
31492                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31493                 }
31494
31495                 if(this.labelmd > 0){
31496                     content[0].cls += ' col-md-' + this.labelmd;
31497                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31498                 }
31499
31500                 if(this.labelsm > 0){
31501                     content[0].cls += ' col-sm-' + this.labelsm;
31502                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31503                 }
31504
31505                 if(this.labelxs > 0){
31506                     content[0].cls += ' col-xs-' + this.labelxs;
31507                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31508                 }
31509                 
31510             }
31511         }
31512         
31513         var cfg = {
31514             tag : 'div',
31515             cls : 'row clearfix',
31516             cn : content
31517         };
31518         
31519         return cfg;
31520         
31521     },
31522     
31523     initEvents : function()
31524     {
31525         this.managerEl = this.el.select('.roo-document-manager', true).first();
31526         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31527         
31528         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31529         this.selectorEl.hide();
31530         
31531         if(this.multiple){
31532             this.selectorEl.attr('multiple', 'multiple');
31533         }
31534         
31535         this.selectorEl.on('change', this.onFileSelected, this);
31536         
31537         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31538         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31539         
31540         this.uploader.on('click', this.onUploaderClick, this);
31541         
31542         this.renderProgressDialog();
31543         
31544         var _this = this;
31545         
31546         window.addEventListener("resize", function() { _this.refresh(); } );
31547         
31548         this.fireEvent('initial', this);
31549     },
31550     
31551     renderProgressDialog : function()
31552     {
31553         var _this = this;
31554         
31555         this.progressDialog = new Roo.bootstrap.Modal({
31556             cls : 'roo-document-manager-progress-dialog',
31557             allow_close : false,
31558             animate : false,
31559             title : '',
31560             buttons : [
31561                 {
31562                     name  :'cancel',
31563                     weight : 'danger',
31564                     html : 'Cancel'
31565                 }
31566             ], 
31567             listeners : { 
31568                 btnclick : function() {
31569                     _this.uploadCancel();
31570                     this.hide();
31571                 }
31572             }
31573         });
31574          
31575         this.progressDialog.render(Roo.get(document.body));
31576          
31577         this.progress = new Roo.bootstrap.Progress({
31578             cls : 'roo-document-manager-progress',
31579             active : true,
31580             striped : true
31581         });
31582         
31583         this.progress.render(this.progressDialog.getChildContainer());
31584         
31585         this.progressBar = new Roo.bootstrap.ProgressBar({
31586             cls : 'roo-document-manager-progress-bar',
31587             aria_valuenow : 0,
31588             aria_valuemin : 0,
31589             aria_valuemax : 12,
31590             panel : 'success'
31591         });
31592         
31593         this.progressBar.render(this.progress.getChildContainer());
31594     },
31595     
31596     onUploaderClick : function(e)
31597     {
31598         e.preventDefault();
31599      
31600         if(this.fireEvent('beforeselectfile', this) != false){
31601             this.selectorEl.dom.click();
31602         }
31603         
31604     },
31605     
31606     onFileSelected : function(e)
31607     {
31608         e.preventDefault();
31609         
31610         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31611             return;
31612         }
31613         
31614         Roo.each(this.selectorEl.dom.files, function(file){
31615             if(this.fireEvent('inspect', this, file) != false){
31616                 this.files.push(file);
31617             }
31618         }, this);
31619         
31620         this.queue();
31621         
31622     },
31623     
31624     queue : function()
31625     {
31626         this.selectorEl.dom.value = '';
31627         
31628         if(!this.files || !this.files.length){
31629             return;
31630         }
31631         
31632         if(this.boxes > 0 && this.files.length > this.boxes){
31633             this.files = this.files.slice(0, this.boxes);
31634         }
31635         
31636         this.uploader.show();
31637         
31638         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31639             this.uploader.hide();
31640         }
31641         
31642         var _this = this;
31643         
31644         var files = [];
31645         
31646         var docs = [];
31647         
31648         Roo.each(this.files, function(file){
31649             
31650             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31651                 var f = this.renderPreview(file);
31652                 files.push(f);
31653                 return;
31654             }
31655             
31656             if(file.type.indexOf('image') != -1){
31657                 this.delegates.push(
31658                     (function(){
31659                         _this.process(file);
31660                     }).createDelegate(this)
31661                 );
31662         
31663                 return;
31664             }
31665             
31666             docs.push(
31667                 (function(){
31668                     _this.process(file);
31669                 }).createDelegate(this)
31670             );
31671             
31672         }, this);
31673         
31674         this.files = files;
31675         
31676         this.delegates = this.delegates.concat(docs);
31677         
31678         if(!this.delegates.length){
31679             this.refresh();
31680             return;
31681         }
31682         
31683         this.progressBar.aria_valuemax = this.delegates.length;
31684         
31685         this.arrange();
31686         
31687         return;
31688     },
31689     
31690     arrange : function()
31691     {
31692         if(!this.delegates.length){
31693             this.progressDialog.hide();
31694             this.refresh();
31695             return;
31696         }
31697         
31698         var delegate = this.delegates.shift();
31699         
31700         this.progressDialog.show();
31701         
31702         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31703         
31704         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31705         
31706         delegate();
31707     },
31708     
31709     refresh : function()
31710     {
31711         this.uploader.show();
31712         
31713         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31714             this.uploader.hide();
31715         }
31716         
31717         Roo.isTouch ? this.closable(false) : this.closable(true);
31718         
31719         this.fireEvent('refresh', this);
31720     },
31721     
31722     onRemove : function(e, el, o)
31723     {
31724         e.preventDefault();
31725         
31726         this.fireEvent('remove', this, o);
31727         
31728     },
31729     
31730     remove : function(o)
31731     {
31732         var files = [];
31733         
31734         Roo.each(this.files, function(file){
31735             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31736                 files.push(file);
31737                 return;
31738             }
31739
31740             o.target.remove();
31741
31742         }, this);
31743         
31744         this.files = files;
31745         
31746         this.refresh();
31747     },
31748     
31749     clear : function()
31750     {
31751         Roo.each(this.files, function(file){
31752             if(!file.target){
31753                 return;
31754             }
31755             
31756             file.target.remove();
31757
31758         }, this);
31759         
31760         this.files = [];
31761         
31762         this.refresh();
31763     },
31764     
31765     onClick : function(e, el, o)
31766     {
31767         e.preventDefault();
31768         
31769         this.fireEvent('click', this, o);
31770         
31771     },
31772     
31773     closable : function(closable)
31774     {
31775         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31776             
31777             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31778             
31779             if(closable){
31780                 el.show();
31781                 return;
31782             }
31783             
31784             el.hide();
31785             
31786         }, this);
31787     },
31788     
31789     xhrOnLoad : function(xhr)
31790     {
31791         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31792             el.remove();
31793         }, this);
31794         
31795         if (xhr.readyState !== 4) {
31796             this.arrange();
31797             this.fireEvent('exception', this, xhr);
31798             return;
31799         }
31800
31801         var response = Roo.decode(xhr.responseText);
31802         
31803         if(!response.success){
31804             this.arrange();
31805             this.fireEvent('exception', this, xhr);
31806             return;
31807         }
31808         
31809         var file = this.renderPreview(response.data);
31810         
31811         this.files.push(file);
31812         
31813         this.arrange();
31814         
31815         this.fireEvent('afterupload', this, xhr);
31816         
31817     },
31818     
31819     xhrOnError : function(xhr)
31820     {
31821         Roo.log('xhr on error');
31822         
31823         var response = Roo.decode(xhr.responseText);
31824           
31825         Roo.log(response);
31826         
31827         this.arrange();
31828     },
31829     
31830     process : function(file)
31831     {
31832         if(this.fireEvent('process', this, file) !== false){
31833             if(this.editable && file.type.indexOf('image') != -1){
31834                 this.fireEvent('edit', this, file);
31835                 return;
31836             }
31837
31838             this.uploadStart(file, false);
31839
31840             return;
31841         }
31842         
31843     },
31844     
31845     uploadStart : function(file, crop)
31846     {
31847         this.xhr = new XMLHttpRequest();
31848         
31849         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31850             this.arrange();
31851             return;
31852         }
31853         
31854         file.xhr = this.xhr;
31855             
31856         this.managerEl.createChild({
31857             tag : 'div',
31858             cls : 'roo-document-manager-loading',
31859             cn : [
31860                 {
31861                     tag : 'div',
31862                     tooltip : file.name,
31863                     cls : 'roo-document-manager-thumb',
31864                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31865                 }
31866             ]
31867
31868         });
31869
31870         this.xhr.open(this.method, this.url, true);
31871         
31872         var headers = {
31873             "Accept": "application/json",
31874             "Cache-Control": "no-cache",
31875             "X-Requested-With": "XMLHttpRequest"
31876         };
31877         
31878         for (var headerName in headers) {
31879             var headerValue = headers[headerName];
31880             if (headerValue) {
31881                 this.xhr.setRequestHeader(headerName, headerValue);
31882             }
31883         }
31884         
31885         var _this = this;
31886         
31887         this.xhr.onload = function()
31888         {
31889             _this.xhrOnLoad(_this.xhr);
31890         }
31891         
31892         this.xhr.onerror = function()
31893         {
31894             _this.xhrOnError(_this.xhr);
31895         }
31896         
31897         var formData = new FormData();
31898
31899         formData.append('returnHTML', 'NO');
31900         
31901         if(crop){
31902             formData.append('crop', crop);
31903         }
31904         
31905         formData.append(this.paramName, file, file.name);
31906         
31907         var options = {
31908             file : file, 
31909             manually : false
31910         };
31911         
31912         if(this.fireEvent('prepare', this, formData, options) != false){
31913             
31914             if(options.manually){
31915                 return;
31916             }
31917             
31918             this.xhr.send(formData);
31919             return;
31920         };
31921         
31922         this.uploadCancel();
31923     },
31924     
31925     uploadCancel : function()
31926     {
31927         if (this.xhr) {
31928             this.xhr.abort();
31929         }
31930         
31931         this.delegates = [];
31932         
31933         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31934             el.remove();
31935         }, this);
31936         
31937         this.arrange();
31938     },
31939     
31940     renderPreview : function(file)
31941     {
31942         if(typeof(file.target) != 'undefined' && file.target){
31943             return file;
31944         }
31945         
31946         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31947         
31948         var previewEl = this.managerEl.createChild({
31949             tag : 'div',
31950             cls : 'roo-document-manager-preview',
31951             cn : [
31952                 {
31953                     tag : 'div',
31954                     tooltip : file[this.toolTipName],
31955                     cls : 'roo-document-manager-thumb',
31956                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31957                 },
31958                 {
31959                     tag : 'button',
31960                     cls : 'close',
31961                     html : '<i class="fa fa-times-circle"></i>'
31962                 }
31963             ]
31964         });
31965
31966         var close = previewEl.select('button.close', true).first();
31967
31968         close.on('click', this.onRemove, this, file);
31969
31970         file.target = previewEl;
31971
31972         var image = previewEl.select('img', true).first();
31973         
31974         var _this = this;
31975         
31976         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31977         
31978         image.on('click', this.onClick, this, file);
31979         
31980         this.fireEvent('previewrendered', this, file);
31981         
31982         return file;
31983         
31984     },
31985     
31986     onPreviewLoad : function(file, image)
31987     {
31988         if(typeof(file.target) == 'undefined' || !file.target){
31989             return;
31990         }
31991         
31992         var width = image.dom.naturalWidth || image.dom.width;
31993         var height = image.dom.naturalHeight || image.dom.height;
31994         
31995         if(!this.previewResize) {
31996             return;
31997         }
31998         
31999         if(width > height){
32000             file.target.addClass('wide');
32001             return;
32002         }
32003         
32004         file.target.addClass('tall');
32005         return;
32006         
32007     },
32008     
32009     uploadFromSource : function(file, crop)
32010     {
32011         this.xhr = new XMLHttpRequest();
32012         
32013         this.managerEl.createChild({
32014             tag : 'div',
32015             cls : 'roo-document-manager-loading',
32016             cn : [
32017                 {
32018                     tag : 'div',
32019                     tooltip : file.name,
32020                     cls : 'roo-document-manager-thumb',
32021                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32022                 }
32023             ]
32024
32025         });
32026
32027         this.xhr.open(this.method, this.url, true);
32028         
32029         var headers = {
32030             "Accept": "application/json",
32031             "Cache-Control": "no-cache",
32032             "X-Requested-With": "XMLHttpRequest"
32033         };
32034         
32035         for (var headerName in headers) {
32036             var headerValue = headers[headerName];
32037             if (headerValue) {
32038                 this.xhr.setRequestHeader(headerName, headerValue);
32039             }
32040         }
32041         
32042         var _this = this;
32043         
32044         this.xhr.onload = function()
32045         {
32046             _this.xhrOnLoad(_this.xhr);
32047         }
32048         
32049         this.xhr.onerror = function()
32050         {
32051             _this.xhrOnError(_this.xhr);
32052         }
32053         
32054         var formData = new FormData();
32055
32056         formData.append('returnHTML', 'NO');
32057         
32058         formData.append('crop', crop);
32059         
32060         if(typeof(file.filename) != 'undefined'){
32061             formData.append('filename', file.filename);
32062         }
32063         
32064         if(typeof(file.mimetype) != 'undefined'){
32065             formData.append('mimetype', file.mimetype);
32066         }
32067         
32068         Roo.log(formData);
32069         
32070         if(this.fireEvent('prepare', this, formData) != false){
32071             this.xhr.send(formData);
32072         };
32073     }
32074 });
32075
32076 /*
32077 * Licence: LGPL
32078 */
32079
32080 /**
32081  * @class Roo.bootstrap.DocumentViewer
32082  * @extends Roo.bootstrap.Component
32083  * Bootstrap DocumentViewer class
32084  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32085  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32086  * 
32087  * @constructor
32088  * Create a new DocumentViewer
32089  * @param {Object} config The config object
32090  */
32091
32092 Roo.bootstrap.DocumentViewer = function(config){
32093     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32094     
32095     this.addEvents({
32096         /**
32097          * @event initial
32098          * Fire after initEvent
32099          * @param {Roo.bootstrap.DocumentViewer} this
32100          */
32101         "initial" : true,
32102         /**
32103          * @event click
32104          * Fire after click
32105          * @param {Roo.bootstrap.DocumentViewer} this
32106          */
32107         "click" : true,
32108         /**
32109          * @event download
32110          * Fire after download button
32111          * @param {Roo.bootstrap.DocumentViewer} this
32112          */
32113         "download" : true,
32114         /**
32115          * @event trash
32116          * Fire after trash button
32117          * @param {Roo.bootstrap.DocumentViewer} this
32118          */
32119         "trash" : true
32120         
32121     });
32122 };
32123
32124 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32125     
32126     showDownload : true,
32127     
32128     showTrash : true,
32129     
32130     getAutoCreate : function()
32131     {
32132         var cfg = {
32133             tag : 'div',
32134             cls : 'roo-document-viewer',
32135             cn : [
32136                 {
32137                     tag : 'div',
32138                     cls : 'roo-document-viewer-body',
32139                     cn : [
32140                         {
32141                             tag : 'div',
32142                             cls : 'roo-document-viewer-thumb',
32143                             cn : [
32144                                 {
32145                                     tag : 'img',
32146                                     cls : 'roo-document-viewer-image'
32147                                 }
32148                             ]
32149                         }
32150                     ]
32151                 },
32152                 {
32153                     tag : 'div',
32154                     cls : 'roo-document-viewer-footer',
32155                     cn : {
32156                         tag : 'div',
32157                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32158                         cn : [
32159                             {
32160                                 tag : 'div',
32161                                 cls : 'btn-group roo-document-viewer-download',
32162                                 cn : [
32163                                     {
32164                                         tag : 'button',
32165                                         cls : 'btn btn-default',
32166                                         html : '<i class="fa fa-download"></i>'
32167                                     }
32168                                 ]
32169                             },
32170                             {
32171                                 tag : 'div',
32172                                 cls : 'btn-group roo-document-viewer-trash',
32173                                 cn : [
32174                                     {
32175                                         tag : 'button',
32176                                         cls : 'btn btn-default',
32177                                         html : '<i class="fa fa-trash"></i>'
32178                                     }
32179                                 ]
32180                             }
32181                         ]
32182                     }
32183                 }
32184             ]
32185         };
32186         
32187         return cfg;
32188     },
32189     
32190     initEvents : function()
32191     {
32192         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32193         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32194         
32195         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32196         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32197         
32198         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32199         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32200         
32201         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32202         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32203         
32204         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32205         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32206         
32207         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32208         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32209         
32210         this.bodyEl.on('click', this.onClick, this);
32211         this.downloadBtn.on('click', this.onDownload, this);
32212         this.trashBtn.on('click', this.onTrash, this);
32213         
32214         this.downloadBtn.hide();
32215         this.trashBtn.hide();
32216         
32217         if(this.showDownload){
32218             this.downloadBtn.show();
32219         }
32220         
32221         if(this.showTrash){
32222             this.trashBtn.show();
32223         }
32224         
32225         if(!this.showDownload && !this.showTrash) {
32226             this.footerEl.hide();
32227         }
32228         
32229     },
32230     
32231     initial : function()
32232     {
32233         this.fireEvent('initial', this);
32234         
32235     },
32236     
32237     onClick : function(e)
32238     {
32239         e.preventDefault();
32240         
32241         this.fireEvent('click', this);
32242     },
32243     
32244     onDownload : function(e)
32245     {
32246         e.preventDefault();
32247         
32248         this.fireEvent('download', this);
32249     },
32250     
32251     onTrash : function(e)
32252     {
32253         e.preventDefault();
32254         
32255         this.fireEvent('trash', this);
32256     }
32257     
32258 });
32259 /*
32260  * - LGPL
32261  *
32262  * nav progress bar
32263  * 
32264  */
32265
32266 /**
32267  * @class Roo.bootstrap.NavProgressBar
32268  * @extends Roo.bootstrap.Component
32269  * Bootstrap NavProgressBar class
32270  * 
32271  * @constructor
32272  * Create a new nav progress bar
32273  * @param {Object} config The config object
32274  */
32275
32276 Roo.bootstrap.NavProgressBar = function(config){
32277     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32278
32279     this.bullets = this.bullets || [];
32280    
32281 //    Roo.bootstrap.NavProgressBar.register(this);
32282      this.addEvents({
32283         /**
32284              * @event changed
32285              * Fires when the active item changes
32286              * @param {Roo.bootstrap.NavProgressBar} this
32287              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32288              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32289          */
32290         'changed': true
32291      });
32292     
32293 };
32294
32295 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32296     
32297     bullets : [],
32298     barItems : [],
32299     
32300     getAutoCreate : function()
32301     {
32302         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32303         
32304         cfg = {
32305             tag : 'div',
32306             cls : 'roo-navigation-bar-group',
32307             cn : [
32308                 {
32309                     tag : 'div',
32310                     cls : 'roo-navigation-top-bar'
32311                 },
32312                 {
32313                     tag : 'div',
32314                     cls : 'roo-navigation-bullets-bar',
32315                     cn : [
32316                         {
32317                             tag : 'ul',
32318                             cls : 'roo-navigation-bar'
32319                         }
32320                     ]
32321                 },
32322                 
32323                 {
32324                     tag : 'div',
32325                     cls : 'roo-navigation-bottom-bar'
32326                 }
32327             ]
32328             
32329         };
32330         
32331         return cfg;
32332         
32333     },
32334     
32335     initEvents: function() 
32336     {
32337         
32338     },
32339     
32340     onRender : function(ct, position) 
32341     {
32342         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32343         
32344         if(this.bullets.length){
32345             Roo.each(this.bullets, function(b){
32346                this.addItem(b);
32347             }, this);
32348         }
32349         
32350         this.format();
32351         
32352     },
32353     
32354     addItem : function(cfg)
32355     {
32356         var item = new Roo.bootstrap.NavProgressItem(cfg);
32357         
32358         item.parentId = this.id;
32359         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32360         
32361         if(cfg.html){
32362             var top = new Roo.bootstrap.Element({
32363                 tag : 'div',
32364                 cls : 'roo-navigation-bar-text'
32365             });
32366             
32367             var bottom = new Roo.bootstrap.Element({
32368                 tag : 'div',
32369                 cls : 'roo-navigation-bar-text'
32370             });
32371             
32372             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32373             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32374             
32375             var topText = new Roo.bootstrap.Element({
32376                 tag : 'span',
32377                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32378             });
32379             
32380             var bottomText = new Roo.bootstrap.Element({
32381                 tag : 'span',
32382                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32383             });
32384             
32385             topText.onRender(top.el, null);
32386             bottomText.onRender(bottom.el, null);
32387             
32388             item.topEl = top;
32389             item.bottomEl = bottom;
32390         }
32391         
32392         this.barItems.push(item);
32393         
32394         return item;
32395     },
32396     
32397     getActive : function()
32398     {
32399         var active = false;
32400         
32401         Roo.each(this.barItems, function(v){
32402             
32403             if (!v.isActive()) {
32404                 return;
32405             }
32406             
32407             active = v;
32408             return false;
32409             
32410         });
32411         
32412         return active;
32413     },
32414     
32415     setActiveItem : function(item)
32416     {
32417         var prev = false;
32418         
32419         Roo.each(this.barItems, function(v){
32420             if (v.rid == item.rid) {
32421                 return ;
32422             }
32423             
32424             if (v.isActive()) {
32425                 v.setActive(false);
32426                 prev = v;
32427             }
32428         });
32429
32430         item.setActive(true);
32431         
32432         this.fireEvent('changed', this, item, prev);
32433     },
32434     
32435     getBarItem: function(rid)
32436     {
32437         var ret = false;
32438         
32439         Roo.each(this.barItems, function(e) {
32440             if (e.rid != rid) {
32441                 return;
32442             }
32443             
32444             ret =  e;
32445             return false;
32446         });
32447         
32448         return ret;
32449     },
32450     
32451     indexOfItem : function(item)
32452     {
32453         var index = false;
32454         
32455         Roo.each(this.barItems, function(v, i){
32456             
32457             if (v.rid != item.rid) {
32458                 return;
32459             }
32460             
32461             index = i;
32462             return false
32463         });
32464         
32465         return index;
32466     },
32467     
32468     setActiveNext : function()
32469     {
32470         var i = this.indexOfItem(this.getActive());
32471         
32472         if (i > this.barItems.length) {
32473             return;
32474         }
32475         
32476         this.setActiveItem(this.barItems[i+1]);
32477     },
32478     
32479     setActivePrev : function()
32480     {
32481         var i = this.indexOfItem(this.getActive());
32482         
32483         if (i  < 1) {
32484             return;
32485         }
32486         
32487         this.setActiveItem(this.barItems[i-1]);
32488     },
32489     
32490     format : function()
32491     {
32492         if(!this.barItems.length){
32493             return;
32494         }
32495      
32496         var width = 100 / this.barItems.length;
32497         
32498         Roo.each(this.barItems, function(i){
32499             i.el.setStyle('width', width + '%');
32500             i.topEl.el.setStyle('width', width + '%');
32501             i.bottomEl.el.setStyle('width', width + '%');
32502         }, this);
32503         
32504     }
32505     
32506 });
32507 /*
32508  * - LGPL
32509  *
32510  * Nav Progress Item
32511  * 
32512  */
32513
32514 /**
32515  * @class Roo.bootstrap.NavProgressItem
32516  * @extends Roo.bootstrap.Component
32517  * Bootstrap NavProgressItem class
32518  * @cfg {String} rid the reference id
32519  * @cfg {Boolean} active (true|false) Is item active default false
32520  * @cfg {Boolean} disabled (true|false) Is item active default false
32521  * @cfg {String} html
32522  * @cfg {String} position (top|bottom) text position default bottom
32523  * @cfg {String} icon show icon instead of number
32524  * 
32525  * @constructor
32526  * Create a new NavProgressItem
32527  * @param {Object} config The config object
32528  */
32529 Roo.bootstrap.NavProgressItem = function(config){
32530     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32531     this.addEvents({
32532         // raw events
32533         /**
32534          * @event click
32535          * The raw click event for the entire grid.
32536          * @param {Roo.bootstrap.NavProgressItem} this
32537          * @param {Roo.EventObject} e
32538          */
32539         "click" : true
32540     });
32541    
32542 };
32543
32544 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32545     
32546     rid : '',
32547     active : false,
32548     disabled : false,
32549     html : '',
32550     position : 'bottom',
32551     icon : false,
32552     
32553     getAutoCreate : function()
32554     {
32555         var iconCls = 'roo-navigation-bar-item-icon';
32556         
32557         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32558         
32559         var cfg = {
32560             tag: 'li',
32561             cls: 'roo-navigation-bar-item',
32562             cn : [
32563                 {
32564                     tag : 'i',
32565                     cls : iconCls
32566                 }
32567             ]
32568         };
32569         
32570         if(this.active){
32571             cfg.cls += ' active';
32572         }
32573         if(this.disabled){
32574             cfg.cls += ' disabled';
32575         }
32576         
32577         return cfg;
32578     },
32579     
32580     disable : function()
32581     {
32582         this.setDisabled(true);
32583     },
32584     
32585     enable : function()
32586     {
32587         this.setDisabled(false);
32588     },
32589     
32590     initEvents: function() 
32591     {
32592         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32593         
32594         this.iconEl.on('click', this.onClick, this);
32595     },
32596     
32597     onClick : function(e)
32598     {
32599         e.preventDefault();
32600         
32601         if(this.disabled){
32602             return;
32603         }
32604         
32605         if(this.fireEvent('click', this, e) === false){
32606             return;
32607         };
32608         
32609         this.parent().setActiveItem(this);
32610     },
32611     
32612     isActive: function () 
32613     {
32614         return this.active;
32615     },
32616     
32617     setActive : function(state)
32618     {
32619         if(this.active == state){
32620             return;
32621         }
32622         
32623         this.active = state;
32624         
32625         if (state) {
32626             this.el.addClass('active');
32627             return;
32628         }
32629         
32630         this.el.removeClass('active');
32631         
32632         return;
32633     },
32634     
32635     setDisabled : function(state)
32636     {
32637         if(this.disabled == state){
32638             return;
32639         }
32640         
32641         this.disabled = state;
32642         
32643         if (state) {
32644             this.el.addClass('disabled');
32645             return;
32646         }
32647         
32648         this.el.removeClass('disabled');
32649     },
32650     
32651     tooltipEl : function()
32652     {
32653         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32654     }
32655 });
32656  
32657
32658  /*
32659  * - LGPL
32660  *
32661  * FieldLabel
32662  * 
32663  */
32664
32665 /**
32666  * @class Roo.bootstrap.FieldLabel
32667  * @extends Roo.bootstrap.Component
32668  * Bootstrap FieldLabel class
32669  * @cfg {String} html contents of the element
32670  * @cfg {String} tag tag of the element default label
32671  * @cfg {String} cls class of the element
32672  * @cfg {String} target label target 
32673  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32674  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32675  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32676  * @cfg {String} iconTooltip default "This field is required"
32677  * @cfg {String} indicatorpos (left|right) default left
32678  * 
32679  * @constructor
32680  * Create a new FieldLabel
32681  * @param {Object} config The config object
32682  */
32683
32684 Roo.bootstrap.FieldLabel = function(config){
32685     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32686     
32687     this.addEvents({
32688             /**
32689              * @event invalid
32690              * Fires after the field has been marked as invalid.
32691              * @param {Roo.form.FieldLabel} this
32692              * @param {String} msg The validation message
32693              */
32694             invalid : true,
32695             /**
32696              * @event valid
32697              * Fires after the field has been validated with no errors.
32698              * @param {Roo.form.FieldLabel} this
32699              */
32700             valid : true
32701         });
32702 };
32703
32704 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32705     
32706     tag: 'label',
32707     cls: '',
32708     html: '',
32709     target: '',
32710     allowBlank : true,
32711     invalidClass : 'has-warning',
32712     validClass : 'has-success',
32713     iconTooltip : 'This field is required',
32714     indicatorpos : 'left',
32715     
32716     getAutoCreate : function(){
32717         
32718         var cls = "";
32719         if (!this.allowBlank) {
32720             cls  = "visible";
32721         }
32722         
32723         var cfg = {
32724             tag : this.tag,
32725             cls : 'roo-bootstrap-field-label ' + this.cls,
32726             for : this.target,
32727             cn : [
32728                 {
32729                     tag : 'i',
32730                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32731                     tooltip : this.iconTooltip
32732                 },
32733                 {
32734                     tag : 'span',
32735                     html : this.html
32736                 }
32737             ] 
32738         };
32739         
32740         if(this.indicatorpos == 'right'){
32741             var cfg = {
32742                 tag : this.tag,
32743                 cls : 'roo-bootstrap-field-label ' + this.cls,
32744                 for : this.target,
32745                 cn : [
32746                     {
32747                         tag : 'span',
32748                         html : this.html
32749                     },
32750                     {
32751                         tag : 'i',
32752                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32753                         tooltip : this.iconTooltip
32754                     }
32755                 ] 
32756             };
32757         }
32758         
32759         return cfg;
32760     },
32761     
32762     initEvents: function() 
32763     {
32764         Roo.bootstrap.Element.superclass.initEvents.call(this);
32765         
32766         this.indicator = this.indicatorEl();
32767         
32768         if(this.indicator){
32769             this.indicator.removeClass('visible');
32770             this.indicator.addClass('invisible');
32771         }
32772         
32773         Roo.bootstrap.FieldLabel.register(this);
32774     },
32775     
32776     indicatorEl : function()
32777     {
32778         var indicator = this.el.select('i.roo-required-indicator',true).first();
32779         
32780         if(!indicator){
32781             return false;
32782         }
32783         
32784         return indicator;
32785         
32786     },
32787     
32788     /**
32789      * Mark this field as valid
32790      */
32791     markValid : function()
32792     {
32793         if(this.indicator){
32794             this.indicator.removeClass('visible');
32795             this.indicator.addClass('invisible');
32796         }
32797         if (Roo.bootstrap.version == 3) {
32798             this.el.removeClass(this.invalidClass);
32799             this.el.addClass(this.validClass);
32800         } else {
32801             this.el.removeClass('is-invalid');
32802             this.el.addClass('is-valid');
32803         }
32804         
32805         
32806         this.fireEvent('valid', this);
32807     },
32808     
32809     /**
32810      * Mark this field as invalid
32811      * @param {String} msg The validation message
32812      */
32813     markInvalid : function(msg)
32814     {
32815         if(this.indicator){
32816             this.indicator.removeClass('invisible');
32817             this.indicator.addClass('visible');
32818         }
32819           if (Roo.bootstrap.version == 3) {
32820             this.el.removeClass(this.validClass);
32821             this.el.addClass(this.invalidClass);
32822         } else {
32823             this.el.removeClass('is-valid');
32824             this.el.addClass('is-invalid');
32825         }
32826         
32827         
32828         this.fireEvent('invalid', this, msg);
32829     }
32830     
32831    
32832 });
32833
32834 Roo.apply(Roo.bootstrap.FieldLabel, {
32835     
32836     groups: {},
32837     
32838      /**
32839     * register a FieldLabel Group
32840     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32841     */
32842     register : function(label)
32843     {
32844         if(this.groups.hasOwnProperty(label.target)){
32845             return;
32846         }
32847      
32848         this.groups[label.target] = label;
32849         
32850     },
32851     /**
32852     * fetch a FieldLabel Group based on the target
32853     * @param {string} target
32854     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32855     */
32856     get: function(target) {
32857         if (typeof(this.groups[target]) == 'undefined') {
32858             return false;
32859         }
32860         
32861         return this.groups[target] ;
32862     }
32863 });
32864
32865  
32866
32867  /*
32868  * - LGPL
32869  *
32870  * page DateSplitField.
32871  * 
32872  */
32873
32874
32875 /**
32876  * @class Roo.bootstrap.DateSplitField
32877  * @extends Roo.bootstrap.Component
32878  * Bootstrap DateSplitField class
32879  * @cfg {string} fieldLabel - the label associated
32880  * @cfg {Number} labelWidth set the width of label (0-12)
32881  * @cfg {String} labelAlign (top|left)
32882  * @cfg {Boolean} dayAllowBlank (true|false) default false
32883  * @cfg {Boolean} monthAllowBlank (true|false) default false
32884  * @cfg {Boolean} yearAllowBlank (true|false) default false
32885  * @cfg {string} dayPlaceholder 
32886  * @cfg {string} monthPlaceholder
32887  * @cfg {string} yearPlaceholder
32888  * @cfg {string} dayFormat default 'd'
32889  * @cfg {string} monthFormat default 'm'
32890  * @cfg {string} yearFormat default 'Y'
32891  * @cfg {Number} labellg set the width of label (1-12)
32892  * @cfg {Number} labelmd set the width of label (1-12)
32893  * @cfg {Number} labelsm set the width of label (1-12)
32894  * @cfg {Number} labelxs set the width of label (1-12)
32895
32896  *     
32897  * @constructor
32898  * Create a new DateSplitField
32899  * @param {Object} config The config object
32900  */
32901
32902 Roo.bootstrap.DateSplitField = function(config){
32903     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32904     
32905     this.addEvents({
32906         // raw events
32907          /**
32908          * @event years
32909          * getting the data of years
32910          * @param {Roo.bootstrap.DateSplitField} this
32911          * @param {Object} years
32912          */
32913         "years" : true,
32914         /**
32915          * @event days
32916          * getting the data of days
32917          * @param {Roo.bootstrap.DateSplitField} this
32918          * @param {Object} days
32919          */
32920         "days" : true,
32921         /**
32922          * @event invalid
32923          * Fires after the field has been marked as invalid.
32924          * @param {Roo.form.Field} this
32925          * @param {String} msg The validation message
32926          */
32927         invalid : true,
32928        /**
32929          * @event valid
32930          * Fires after the field has been validated with no errors.
32931          * @param {Roo.form.Field} this
32932          */
32933         valid : true
32934     });
32935 };
32936
32937 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32938     
32939     fieldLabel : '',
32940     labelAlign : 'top',
32941     labelWidth : 3,
32942     dayAllowBlank : false,
32943     monthAllowBlank : false,
32944     yearAllowBlank : false,
32945     dayPlaceholder : '',
32946     monthPlaceholder : '',
32947     yearPlaceholder : '',
32948     dayFormat : 'd',
32949     monthFormat : 'm',
32950     yearFormat : 'Y',
32951     isFormField : true,
32952     labellg : 0,
32953     labelmd : 0,
32954     labelsm : 0,
32955     labelxs : 0,
32956     
32957     getAutoCreate : function()
32958     {
32959         var cfg = {
32960             tag : 'div',
32961             cls : 'row roo-date-split-field-group',
32962             cn : [
32963                 {
32964                     tag : 'input',
32965                     type : 'hidden',
32966                     cls : 'form-hidden-field roo-date-split-field-group-value',
32967                     name : this.name
32968                 }
32969             ]
32970         };
32971         
32972         var labelCls = 'col-md-12';
32973         var contentCls = 'col-md-4';
32974         
32975         if(this.fieldLabel){
32976             
32977             var label = {
32978                 tag : 'div',
32979                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32980                 cn : [
32981                     {
32982                         tag : 'label',
32983                         html : this.fieldLabel
32984                     }
32985                 ]
32986             };
32987             
32988             if(this.labelAlign == 'left'){
32989             
32990                 if(this.labelWidth > 12){
32991                     label.style = "width: " + this.labelWidth + 'px';
32992                 }
32993
32994                 if(this.labelWidth < 13 && this.labelmd == 0){
32995                     this.labelmd = this.labelWidth;
32996                 }
32997
32998                 if(this.labellg > 0){
32999                     labelCls = ' col-lg-' + this.labellg;
33000                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33001                 }
33002
33003                 if(this.labelmd > 0){
33004                     labelCls = ' col-md-' + this.labelmd;
33005                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33006                 }
33007
33008                 if(this.labelsm > 0){
33009                     labelCls = ' col-sm-' + this.labelsm;
33010                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33011                 }
33012
33013                 if(this.labelxs > 0){
33014                     labelCls = ' col-xs-' + this.labelxs;
33015                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33016                 }
33017             }
33018             
33019             label.cls += ' ' + labelCls;
33020             
33021             cfg.cn.push(label);
33022         }
33023         
33024         Roo.each(['day', 'month', 'year'], function(t){
33025             cfg.cn.push({
33026                 tag : 'div',
33027                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33028             });
33029         }, this);
33030         
33031         return cfg;
33032     },
33033     
33034     inputEl: function ()
33035     {
33036         return this.el.select('.roo-date-split-field-group-value', true).first();
33037     },
33038     
33039     onRender : function(ct, position) 
33040     {
33041         var _this = this;
33042         
33043         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33044         
33045         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33046         
33047         this.dayField = new Roo.bootstrap.ComboBox({
33048             allowBlank : this.dayAllowBlank,
33049             alwaysQuery : true,
33050             displayField : 'value',
33051             editable : false,
33052             fieldLabel : '',
33053             forceSelection : true,
33054             mode : 'local',
33055             placeholder : this.dayPlaceholder,
33056             selectOnFocus : true,
33057             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33058             triggerAction : 'all',
33059             typeAhead : true,
33060             valueField : 'value',
33061             store : new Roo.data.SimpleStore({
33062                 data : (function() {    
33063                     var days = [];
33064                     _this.fireEvent('days', _this, days);
33065                     return days;
33066                 })(),
33067                 fields : [ 'value' ]
33068             }),
33069             listeners : {
33070                 select : function (_self, record, index)
33071                 {
33072                     _this.setValue(_this.getValue());
33073                 }
33074             }
33075         });
33076
33077         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33078         
33079         this.monthField = new Roo.bootstrap.MonthField({
33080             after : '<i class=\"fa fa-calendar\"></i>',
33081             allowBlank : this.monthAllowBlank,
33082             placeholder : this.monthPlaceholder,
33083             readOnly : true,
33084             listeners : {
33085                 render : function (_self)
33086                 {
33087                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33088                         e.preventDefault();
33089                         _self.focus();
33090                     });
33091                 },
33092                 select : function (_self, oldvalue, newvalue)
33093                 {
33094                     _this.setValue(_this.getValue());
33095                 }
33096             }
33097         });
33098         
33099         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33100         
33101         this.yearField = new Roo.bootstrap.ComboBox({
33102             allowBlank : this.yearAllowBlank,
33103             alwaysQuery : true,
33104             displayField : 'value',
33105             editable : false,
33106             fieldLabel : '',
33107             forceSelection : true,
33108             mode : 'local',
33109             placeholder : this.yearPlaceholder,
33110             selectOnFocus : true,
33111             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33112             triggerAction : 'all',
33113             typeAhead : true,
33114             valueField : 'value',
33115             store : new Roo.data.SimpleStore({
33116                 data : (function() {
33117                     var years = [];
33118                     _this.fireEvent('years', _this, years);
33119                     return years;
33120                 })(),
33121                 fields : [ 'value' ]
33122             }),
33123             listeners : {
33124                 select : function (_self, record, index)
33125                 {
33126                     _this.setValue(_this.getValue());
33127                 }
33128             }
33129         });
33130
33131         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33132     },
33133     
33134     setValue : function(v, format)
33135     {
33136         this.inputEl.dom.value = v;
33137         
33138         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33139         
33140         var d = Date.parseDate(v, f);
33141         
33142         if(!d){
33143             this.validate();
33144             return;
33145         }
33146         
33147         this.setDay(d.format(this.dayFormat));
33148         this.setMonth(d.format(this.monthFormat));
33149         this.setYear(d.format(this.yearFormat));
33150         
33151         this.validate();
33152         
33153         return;
33154     },
33155     
33156     setDay : function(v)
33157     {
33158         this.dayField.setValue(v);
33159         this.inputEl.dom.value = this.getValue();
33160         this.validate();
33161         return;
33162     },
33163     
33164     setMonth : function(v)
33165     {
33166         this.monthField.setValue(v, true);
33167         this.inputEl.dom.value = this.getValue();
33168         this.validate();
33169         return;
33170     },
33171     
33172     setYear : function(v)
33173     {
33174         this.yearField.setValue(v);
33175         this.inputEl.dom.value = this.getValue();
33176         this.validate();
33177         return;
33178     },
33179     
33180     getDay : function()
33181     {
33182         return this.dayField.getValue();
33183     },
33184     
33185     getMonth : function()
33186     {
33187         return this.monthField.getValue();
33188     },
33189     
33190     getYear : function()
33191     {
33192         return this.yearField.getValue();
33193     },
33194     
33195     getValue : function()
33196     {
33197         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33198         
33199         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33200         
33201         return date;
33202     },
33203     
33204     reset : function()
33205     {
33206         this.setDay('');
33207         this.setMonth('');
33208         this.setYear('');
33209         this.inputEl.dom.value = '';
33210         this.validate();
33211         return;
33212     },
33213     
33214     validate : function()
33215     {
33216         var d = this.dayField.validate();
33217         var m = this.monthField.validate();
33218         var y = this.yearField.validate();
33219         
33220         var valid = true;
33221         
33222         if(
33223                 (!this.dayAllowBlank && !d) ||
33224                 (!this.monthAllowBlank && !m) ||
33225                 (!this.yearAllowBlank && !y)
33226         ){
33227             valid = false;
33228         }
33229         
33230         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33231             return valid;
33232         }
33233         
33234         if(valid){
33235             this.markValid();
33236             return valid;
33237         }
33238         
33239         this.markInvalid();
33240         
33241         return valid;
33242     },
33243     
33244     markValid : function()
33245     {
33246         
33247         var label = this.el.select('label', true).first();
33248         var icon = this.el.select('i.fa-star', true).first();
33249
33250         if(label && icon){
33251             icon.remove();
33252         }
33253         
33254         this.fireEvent('valid', this);
33255     },
33256     
33257      /**
33258      * Mark this field as invalid
33259      * @param {String} msg The validation message
33260      */
33261     markInvalid : function(msg)
33262     {
33263         
33264         var label = this.el.select('label', true).first();
33265         var icon = this.el.select('i.fa-star', true).first();
33266
33267         if(label && !icon){
33268             this.el.select('.roo-date-split-field-label', true).createChild({
33269                 tag : 'i',
33270                 cls : 'text-danger fa fa-lg fa-star',
33271                 tooltip : 'This field is required',
33272                 style : 'margin-right:5px;'
33273             }, label, true);
33274         }
33275         
33276         this.fireEvent('invalid', this, msg);
33277     },
33278     
33279     clearInvalid : function()
33280     {
33281         var label = this.el.select('label', true).first();
33282         var icon = this.el.select('i.fa-star', true).first();
33283
33284         if(label && icon){
33285             icon.remove();
33286         }
33287         
33288         this.fireEvent('valid', this);
33289     },
33290     
33291     getName: function()
33292     {
33293         return this.name;
33294     }
33295     
33296 });
33297
33298  /**
33299  *
33300  * This is based on 
33301  * http://masonry.desandro.com
33302  *
33303  * The idea is to render all the bricks based on vertical width...
33304  *
33305  * The original code extends 'outlayer' - we might need to use that....
33306  * 
33307  */
33308
33309
33310 /**
33311  * @class Roo.bootstrap.LayoutMasonry
33312  * @extends Roo.bootstrap.Component
33313  * Bootstrap Layout Masonry class
33314  * 
33315  * @constructor
33316  * Create a new Element
33317  * @param {Object} config The config object
33318  */
33319
33320 Roo.bootstrap.LayoutMasonry = function(config){
33321     
33322     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33323     
33324     this.bricks = [];
33325     
33326     Roo.bootstrap.LayoutMasonry.register(this);
33327     
33328     this.addEvents({
33329         // raw events
33330         /**
33331          * @event layout
33332          * Fire after layout the items
33333          * @param {Roo.bootstrap.LayoutMasonry} this
33334          * @param {Roo.EventObject} e
33335          */
33336         "layout" : true
33337     });
33338     
33339 };
33340
33341 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33342     
33343     /**
33344      * @cfg {Boolean} isLayoutInstant = no animation?
33345      */   
33346     isLayoutInstant : false, // needed?
33347    
33348     /**
33349      * @cfg {Number} boxWidth  width of the columns
33350      */   
33351     boxWidth : 450,
33352     
33353       /**
33354      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33355      */   
33356     boxHeight : 0,
33357     
33358     /**
33359      * @cfg {Number} padWidth padding below box..
33360      */   
33361     padWidth : 10, 
33362     
33363     /**
33364      * @cfg {Number} gutter gutter width..
33365      */   
33366     gutter : 10,
33367     
33368      /**
33369      * @cfg {Number} maxCols maximum number of columns
33370      */   
33371     
33372     maxCols: 0,
33373     
33374     /**
33375      * @cfg {Boolean} isAutoInitial defalut true
33376      */   
33377     isAutoInitial : true, 
33378     
33379     containerWidth: 0,
33380     
33381     /**
33382      * @cfg {Boolean} isHorizontal defalut false
33383      */   
33384     isHorizontal : false, 
33385
33386     currentSize : null,
33387     
33388     tag: 'div',
33389     
33390     cls: '',
33391     
33392     bricks: null, //CompositeElement
33393     
33394     cols : 1,
33395     
33396     _isLayoutInited : false,
33397     
33398 //    isAlternative : false, // only use for vertical layout...
33399     
33400     /**
33401      * @cfg {Number} alternativePadWidth padding below box..
33402      */   
33403     alternativePadWidth : 50,
33404     
33405     selectedBrick : [],
33406     
33407     getAutoCreate : function(){
33408         
33409         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33410         
33411         var cfg = {
33412             tag: this.tag,
33413             cls: 'blog-masonary-wrapper ' + this.cls,
33414             cn : {
33415                 cls : 'mas-boxes masonary'
33416             }
33417         };
33418         
33419         return cfg;
33420     },
33421     
33422     getChildContainer: function( )
33423     {
33424         if (this.boxesEl) {
33425             return this.boxesEl;
33426         }
33427         
33428         this.boxesEl = this.el.select('.mas-boxes').first();
33429         
33430         return this.boxesEl;
33431     },
33432     
33433     
33434     initEvents : function()
33435     {
33436         var _this = this;
33437         
33438         if(this.isAutoInitial){
33439             Roo.log('hook children rendered');
33440             this.on('childrenrendered', function() {
33441                 Roo.log('children rendered');
33442                 _this.initial();
33443             } ,this);
33444         }
33445     },
33446     
33447     initial : function()
33448     {
33449         this.selectedBrick = [];
33450         
33451         this.currentSize = this.el.getBox(true);
33452         
33453         Roo.EventManager.onWindowResize(this.resize, this); 
33454
33455         if(!this.isAutoInitial){
33456             this.layout();
33457             return;
33458         }
33459         
33460         this.layout();
33461         
33462         return;
33463         //this.layout.defer(500,this);
33464         
33465     },
33466     
33467     resize : function()
33468     {
33469         var cs = this.el.getBox(true);
33470         
33471         if (
33472                 this.currentSize.width == cs.width && 
33473                 this.currentSize.x == cs.x && 
33474                 this.currentSize.height == cs.height && 
33475                 this.currentSize.y == cs.y 
33476         ) {
33477             Roo.log("no change in with or X or Y");
33478             return;
33479         }
33480         
33481         this.currentSize = cs;
33482         
33483         this.layout();
33484         
33485     },
33486     
33487     layout : function()
33488     {   
33489         this._resetLayout();
33490         
33491         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33492         
33493         this.layoutItems( isInstant );
33494       
33495         this._isLayoutInited = true;
33496         
33497         this.fireEvent('layout', this);
33498         
33499     },
33500     
33501     _resetLayout : function()
33502     {
33503         if(this.isHorizontal){
33504             this.horizontalMeasureColumns();
33505             return;
33506         }
33507         
33508         this.verticalMeasureColumns();
33509         
33510     },
33511     
33512     verticalMeasureColumns : function()
33513     {
33514         this.getContainerWidth();
33515         
33516 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33517 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33518 //            return;
33519 //        }
33520         
33521         var boxWidth = this.boxWidth + this.padWidth;
33522         
33523         if(this.containerWidth < this.boxWidth){
33524             boxWidth = this.containerWidth
33525         }
33526         
33527         var containerWidth = this.containerWidth;
33528         
33529         var cols = Math.floor(containerWidth / boxWidth);
33530         
33531         this.cols = Math.max( cols, 1 );
33532         
33533         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33534         
33535         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33536         
33537         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33538         
33539         this.colWidth = boxWidth + avail - this.padWidth;
33540         
33541         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33542         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33543     },
33544     
33545     horizontalMeasureColumns : function()
33546     {
33547         this.getContainerWidth();
33548         
33549         var boxWidth = this.boxWidth;
33550         
33551         if(this.containerWidth < boxWidth){
33552             boxWidth = this.containerWidth;
33553         }
33554         
33555         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33556         
33557         this.el.setHeight(boxWidth);
33558         
33559     },
33560     
33561     getContainerWidth : function()
33562     {
33563         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33564     },
33565     
33566     layoutItems : function( isInstant )
33567     {
33568         Roo.log(this.bricks);
33569         
33570         var items = Roo.apply([], this.bricks);
33571         
33572         if(this.isHorizontal){
33573             this._horizontalLayoutItems( items , isInstant );
33574             return;
33575         }
33576         
33577 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33578 //            this._verticalAlternativeLayoutItems( items , isInstant );
33579 //            return;
33580 //        }
33581         
33582         this._verticalLayoutItems( items , isInstant );
33583         
33584     },
33585     
33586     _verticalLayoutItems : function ( items , isInstant)
33587     {
33588         if ( !items || !items.length ) {
33589             return;
33590         }
33591         
33592         var standard = [
33593             ['xs', 'xs', 'xs', 'tall'],
33594             ['xs', 'xs', 'tall'],
33595             ['xs', 'xs', 'sm'],
33596             ['xs', 'xs', 'xs'],
33597             ['xs', 'tall'],
33598             ['xs', 'sm'],
33599             ['xs', 'xs'],
33600             ['xs'],
33601             
33602             ['sm', 'xs', 'xs'],
33603             ['sm', 'xs'],
33604             ['sm'],
33605             
33606             ['tall', 'xs', 'xs', 'xs'],
33607             ['tall', 'xs', 'xs'],
33608             ['tall', 'xs'],
33609             ['tall']
33610             
33611         ];
33612         
33613         var queue = [];
33614         
33615         var boxes = [];
33616         
33617         var box = [];
33618         
33619         Roo.each(items, function(item, k){
33620             
33621             switch (item.size) {
33622                 // these layouts take up a full box,
33623                 case 'md' :
33624                 case 'md-left' :
33625                 case 'md-right' :
33626                 case 'wide' :
33627                     
33628                     if(box.length){
33629                         boxes.push(box);
33630                         box = [];
33631                     }
33632                     
33633                     boxes.push([item]);
33634                     
33635                     break;
33636                     
33637                 case 'xs' :
33638                 case 'sm' :
33639                 case 'tall' :
33640                     
33641                     box.push(item);
33642                     
33643                     break;
33644                 default :
33645                     break;
33646                     
33647             }
33648             
33649         }, this);
33650         
33651         if(box.length){
33652             boxes.push(box);
33653             box = [];
33654         }
33655         
33656         var filterPattern = function(box, length)
33657         {
33658             if(!box.length){
33659                 return;
33660             }
33661             
33662             var match = false;
33663             
33664             var pattern = box.slice(0, length);
33665             
33666             var format = [];
33667             
33668             Roo.each(pattern, function(i){
33669                 format.push(i.size);
33670             }, this);
33671             
33672             Roo.each(standard, function(s){
33673                 
33674                 if(String(s) != String(format)){
33675                     return;
33676                 }
33677                 
33678                 match = true;
33679                 return false;
33680                 
33681             }, this);
33682             
33683             if(!match && length == 1){
33684                 return;
33685             }
33686             
33687             if(!match){
33688                 filterPattern(box, length - 1);
33689                 return;
33690             }
33691                 
33692             queue.push(pattern);
33693
33694             box = box.slice(length, box.length);
33695
33696             filterPattern(box, 4);
33697
33698             return;
33699             
33700         }
33701         
33702         Roo.each(boxes, function(box, k){
33703             
33704             if(!box.length){
33705                 return;
33706             }
33707             
33708             if(box.length == 1){
33709                 queue.push(box);
33710                 return;
33711             }
33712             
33713             filterPattern(box, 4);
33714             
33715         }, this);
33716         
33717         this._processVerticalLayoutQueue( queue, isInstant );
33718         
33719     },
33720     
33721 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33722 //    {
33723 //        if ( !items || !items.length ) {
33724 //            return;
33725 //        }
33726 //
33727 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33728 //        
33729 //    },
33730     
33731     _horizontalLayoutItems : function ( items , isInstant)
33732     {
33733         if ( !items || !items.length || items.length < 3) {
33734             return;
33735         }
33736         
33737         items.reverse();
33738         
33739         var eItems = items.slice(0, 3);
33740         
33741         items = items.slice(3, items.length);
33742         
33743         var standard = [
33744             ['xs', 'xs', 'xs', 'wide'],
33745             ['xs', 'xs', 'wide'],
33746             ['xs', 'xs', 'sm'],
33747             ['xs', 'xs', 'xs'],
33748             ['xs', 'wide'],
33749             ['xs', 'sm'],
33750             ['xs', 'xs'],
33751             ['xs'],
33752             
33753             ['sm', 'xs', 'xs'],
33754             ['sm', 'xs'],
33755             ['sm'],
33756             
33757             ['wide', 'xs', 'xs', 'xs'],
33758             ['wide', 'xs', 'xs'],
33759             ['wide', 'xs'],
33760             ['wide'],
33761             
33762             ['wide-thin']
33763         ];
33764         
33765         var queue = [];
33766         
33767         var boxes = [];
33768         
33769         var box = [];
33770         
33771         Roo.each(items, function(item, k){
33772             
33773             switch (item.size) {
33774                 case 'md' :
33775                 case 'md-left' :
33776                 case 'md-right' :
33777                 case 'tall' :
33778                     
33779                     if(box.length){
33780                         boxes.push(box);
33781                         box = [];
33782                     }
33783                     
33784                     boxes.push([item]);
33785                     
33786                     break;
33787                     
33788                 case 'xs' :
33789                 case 'sm' :
33790                 case 'wide' :
33791                 case 'wide-thin' :
33792                     
33793                     box.push(item);
33794                     
33795                     break;
33796                 default :
33797                     break;
33798                     
33799             }
33800             
33801         }, this);
33802         
33803         if(box.length){
33804             boxes.push(box);
33805             box = [];
33806         }
33807         
33808         var filterPattern = function(box, length)
33809         {
33810             if(!box.length){
33811                 return;
33812             }
33813             
33814             var match = false;
33815             
33816             var pattern = box.slice(0, length);
33817             
33818             var format = [];
33819             
33820             Roo.each(pattern, function(i){
33821                 format.push(i.size);
33822             }, this);
33823             
33824             Roo.each(standard, function(s){
33825                 
33826                 if(String(s) != String(format)){
33827                     return;
33828                 }
33829                 
33830                 match = true;
33831                 return false;
33832                 
33833             }, this);
33834             
33835             if(!match && length == 1){
33836                 return;
33837             }
33838             
33839             if(!match){
33840                 filterPattern(box, length - 1);
33841                 return;
33842             }
33843                 
33844             queue.push(pattern);
33845
33846             box = box.slice(length, box.length);
33847
33848             filterPattern(box, 4);
33849
33850             return;
33851             
33852         }
33853         
33854         Roo.each(boxes, function(box, k){
33855             
33856             if(!box.length){
33857                 return;
33858             }
33859             
33860             if(box.length == 1){
33861                 queue.push(box);
33862                 return;
33863             }
33864             
33865             filterPattern(box, 4);
33866             
33867         }, this);
33868         
33869         
33870         var prune = [];
33871         
33872         var pos = this.el.getBox(true);
33873         
33874         var minX = pos.x;
33875         
33876         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33877         
33878         var hit_end = false;
33879         
33880         Roo.each(queue, function(box){
33881             
33882             if(hit_end){
33883                 
33884                 Roo.each(box, function(b){
33885                 
33886                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33887                     b.el.hide();
33888
33889                 }, this);
33890
33891                 return;
33892             }
33893             
33894             var mx = 0;
33895             
33896             Roo.each(box, function(b){
33897                 
33898                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33899                 b.el.show();
33900
33901                 mx = Math.max(mx, b.x);
33902                 
33903             }, this);
33904             
33905             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33906             
33907             if(maxX < minX){
33908                 
33909                 Roo.each(box, function(b){
33910                 
33911                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33912                     b.el.hide();
33913                     
33914                 }, this);
33915                 
33916                 hit_end = true;
33917                 
33918                 return;
33919             }
33920             
33921             prune.push(box);
33922             
33923         }, this);
33924         
33925         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33926     },
33927     
33928     /** Sets position of item in DOM
33929     * @param {Element} item
33930     * @param {Number} x - horizontal position
33931     * @param {Number} y - vertical position
33932     * @param {Boolean} isInstant - disables transitions
33933     */
33934     _processVerticalLayoutQueue : function( queue, isInstant )
33935     {
33936         var pos = this.el.getBox(true);
33937         var x = pos.x;
33938         var y = pos.y;
33939         var maxY = [];
33940         
33941         for (var i = 0; i < this.cols; i++){
33942             maxY[i] = pos.y;
33943         }
33944         
33945         Roo.each(queue, function(box, k){
33946             
33947             var col = k % this.cols;
33948             
33949             Roo.each(box, function(b,kk){
33950                 
33951                 b.el.position('absolute');
33952                 
33953                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33954                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33955                 
33956                 if(b.size == 'md-left' || b.size == 'md-right'){
33957                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33958                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33959                 }
33960                 
33961                 b.el.setWidth(width);
33962                 b.el.setHeight(height);
33963                 // iframe?
33964                 b.el.select('iframe',true).setSize(width,height);
33965                 
33966             }, this);
33967             
33968             for (var i = 0; i < this.cols; i++){
33969                 
33970                 if(maxY[i] < maxY[col]){
33971                     col = i;
33972                     continue;
33973                 }
33974                 
33975                 col = Math.min(col, i);
33976                 
33977             }
33978             
33979             x = pos.x + col * (this.colWidth + this.padWidth);
33980             
33981             y = maxY[col];
33982             
33983             var positions = [];
33984             
33985             switch (box.length){
33986                 case 1 :
33987                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33988                     break;
33989                 case 2 :
33990                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33991                     break;
33992                 case 3 :
33993                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33994                     break;
33995                 case 4 :
33996                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33997                     break;
33998                 default :
33999                     break;
34000             }
34001             
34002             Roo.each(box, function(b,kk){
34003                 
34004                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34005                 
34006                 var sz = b.el.getSize();
34007                 
34008                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34009                 
34010             }, this);
34011             
34012         }, this);
34013         
34014         var mY = 0;
34015         
34016         for (var i = 0; i < this.cols; i++){
34017             mY = Math.max(mY, maxY[i]);
34018         }
34019         
34020         this.el.setHeight(mY - pos.y);
34021         
34022     },
34023     
34024 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34025 //    {
34026 //        var pos = this.el.getBox(true);
34027 //        var x = pos.x;
34028 //        var y = pos.y;
34029 //        var maxX = pos.right;
34030 //        
34031 //        var maxHeight = 0;
34032 //        
34033 //        Roo.each(items, function(item, k){
34034 //            
34035 //            var c = k % 2;
34036 //            
34037 //            item.el.position('absolute');
34038 //                
34039 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34040 //
34041 //            item.el.setWidth(width);
34042 //
34043 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34044 //
34045 //            item.el.setHeight(height);
34046 //            
34047 //            if(c == 0){
34048 //                item.el.setXY([x, y], isInstant ? false : true);
34049 //            } else {
34050 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34051 //            }
34052 //            
34053 //            y = y + height + this.alternativePadWidth;
34054 //            
34055 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34056 //            
34057 //        }, this);
34058 //        
34059 //        this.el.setHeight(maxHeight);
34060 //        
34061 //    },
34062     
34063     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34064     {
34065         var pos = this.el.getBox(true);
34066         
34067         var minX = pos.x;
34068         var minY = pos.y;
34069         
34070         var maxX = pos.right;
34071         
34072         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34073         
34074         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34075         
34076         Roo.each(queue, function(box, k){
34077             
34078             Roo.each(box, function(b, kk){
34079                 
34080                 b.el.position('absolute');
34081                 
34082                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34083                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34084                 
34085                 if(b.size == 'md-left' || b.size == 'md-right'){
34086                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34087                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34088                 }
34089                 
34090                 b.el.setWidth(width);
34091                 b.el.setHeight(height);
34092                 
34093             }, this);
34094             
34095             if(!box.length){
34096                 return;
34097             }
34098             
34099             var positions = [];
34100             
34101             switch (box.length){
34102                 case 1 :
34103                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34104                     break;
34105                 case 2 :
34106                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34107                     break;
34108                 case 3 :
34109                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34110                     break;
34111                 case 4 :
34112                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34113                     break;
34114                 default :
34115                     break;
34116             }
34117             
34118             Roo.each(box, function(b,kk){
34119                 
34120                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34121                 
34122                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34123                 
34124             }, this);
34125             
34126         }, this);
34127         
34128     },
34129     
34130     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34131     {
34132         Roo.each(eItems, function(b,k){
34133             
34134             b.size = (k == 0) ? 'sm' : 'xs';
34135             b.x = (k == 0) ? 2 : 1;
34136             b.y = (k == 0) ? 2 : 1;
34137             
34138             b.el.position('absolute');
34139             
34140             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34141                 
34142             b.el.setWidth(width);
34143             
34144             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34145             
34146             b.el.setHeight(height);
34147             
34148         }, this);
34149
34150         var positions = [];
34151         
34152         positions.push({
34153             x : maxX - this.unitWidth * 2 - this.gutter,
34154             y : minY
34155         });
34156         
34157         positions.push({
34158             x : maxX - this.unitWidth,
34159             y : minY + (this.unitWidth + this.gutter) * 2
34160         });
34161         
34162         positions.push({
34163             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34164             y : minY
34165         });
34166         
34167         Roo.each(eItems, function(b,k){
34168             
34169             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34170
34171         }, this);
34172         
34173     },
34174     
34175     getVerticalOneBoxColPositions : function(x, y, box)
34176     {
34177         var pos = [];
34178         
34179         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34180         
34181         if(box[0].size == 'md-left'){
34182             rand = 0;
34183         }
34184         
34185         if(box[0].size == 'md-right'){
34186             rand = 1;
34187         }
34188         
34189         pos.push({
34190             x : x + (this.unitWidth + this.gutter) * rand,
34191             y : y
34192         });
34193         
34194         return pos;
34195     },
34196     
34197     getVerticalTwoBoxColPositions : function(x, y, box)
34198     {
34199         var pos = [];
34200         
34201         if(box[0].size == 'xs'){
34202             
34203             pos.push({
34204                 x : x,
34205                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34206             });
34207
34208             pos.push({
34209                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34210                 y : y
34211             });
34212             
34213             return pos;
34214             
34215         }
34216         
34217         pos.push({
34218             x : x,
34219             y : y
34220         });
34221
34222         pos.push({
34223             x : x + (this.unitWidth + this.gutter) * 2,
34224             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34225         });
34226         
34227         return pos;
34228         
34229     },
34230     
34231     getVerticalThreeBoxColPositions : function(x, y, box)
34232     {
34233         var pos = [];
34234         
34235         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34236             
34237             pos.push({
34238                 x : x,
34239                 y : y
34240             });
34241
34242             pos.push({
34243                 x : x + (this.unitWidth + this.gutter) * 1,
34244                 y : y
34245             });
34246             
34247             pos.push({
34248                 x : x + (this.unitWidth + this.gutter) * 2,
34249                 y : y
34250             });
34251             
34252             return pos;
34253             
34254         }
34255         
34256         if(box[0].size == 'xs' && box[1].size == 'xs'){
34257             
34258             pos.push({
34259                 x : x,
34260                 y : y
34261             });
34262
34263             pos.push({
34264                 x : x,
34265                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34266             });
34267             
34268             pos.push({
34269                 x : x + (this.unitWidth + this.gutter) * 1,
34270                 y : y
34271             });
34272             
34273             return pos;
34274             
34275         }
34276         
34277         pos.push({
34278             x : x,
34279             y : y
34280         });
34281
34282         pos.push({
34283             x : x + (this.unitWidth + this.gutter) * 2,
34284             y : y
34285         });
34286
34287         pos.push({
34288             x : x + (this.unitWidth + this.gutter) * 2,
34289             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34290         });
34291             
34292         return pos;
34293         
34294     },
34295     
34296     getVerticalFourBoxColPositions : function(x, y, box)
34297     {
34298         var pos = [];
34299         
34300         if(box[0].size == 'xs'){
34301             
34302             pos.push({
34303                 x : x,
34304                 y : y
34305             });
34306
34307             pos.push({
34308                 x : x,
34309                 y : y + (this.unitHeight + this.gutter) * 1
34310             });
34311             
34312             pos.push({
34313                 x : x,
34314                 y : y + (this.unitHeight + this.gutter) * 2
34315             });
34316             
34317             pos.push({
34318                 x : x + (this.unitWidth + this.gutter) * 1,
34319                 y : y
34320             });
34321             
34322             return pos;
34323             
34324         }
34325         
34326         pos.push({
34327             x : x,
34328             y : y
34329         });
34330
34331         pos.push({
34332             x : x + (this.unitWidth + this.gutter) * 2,
34333             y : y
34334         });
34335
34336         pos.push({
34337             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34338             y : y + (this.unitHeight + this.gutter) * 1
34339         });
34340
34341         pos.push({
34342             x : x + (this.unitWidth + this.gutter) * 2,
34343             y : y + (this.unitWidth + this.gutter) * 2
34344         });
34345
34346         return pos;
34347         
34348     },
34349     
34350     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34351     {
34352         var pos = [];
34353         
34354         if(box[0].size == 'md-left'){
34355             pos.push({
34356                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34357                 y : minY
34358             });
34359             
34360             return pos;
34361         }
34362         
34363         if(box[0].size == 'md-right'){
34364             pos.push({
34365                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34366                 y : minY + (this.unitWidth + this.gutter) * 1
34367             });
34368             
34369             return pos;
34370         }
34371         
34372         var rand = Math.floor(Math.random() * (4 - box[0].y));
34373         
34374         pos.push({
34375             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34376             y : minY + (this.unitWidth + this.gutter) * rand
34377         });
34378         
34379         return pos;
34380         
34381     },
34382     
34383     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34384     {
34385         var pos = [];
34386         
34387         if(box[0].size == 'xs'){
34388             
34389             pos.push({
34390                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34391                 y : minY
34392             });
34393
34394             pos.push({
34395                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34396                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34397             });
34398             
34399             return pos;
34400             
34401         }
34402         
34403         pos.push({
34404             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34405             y : minY
34406         });
34407
34408         pos.push({
34409             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34410             y : minY + (this.unitWidth + this.gutter) * 2
34411         });
34412         
34413         return pos;
34414         
34415     },
34416     
34417     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34418     {
34419         var pos = [];
34420         
34421         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34422             
34423             pos.push({
34424                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34425                 y : minY
34426             });
34427
34428             pos.push({
34429                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34430                 y : minY + (this.unitWidth + this.gutter) * 1
34431             });
34432             
34433             pos.push({
34434                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34435                 y : minY + (this.unitWidth + this.gutter) * 2
34436             });
34437             
34438             return pos;
34439             
34440         }
34441         
34442         if(box[0].size == 'xs' && box[1].size == 'xs'){
34443             
34444             pos.push({
34445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34446                 y : minY
34447             });
34448
34449             pos.push({
34450                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34451                 y : minY
34452             });
34453             
34454             pos.push({
34455                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34456                 y : minY + (this.unitWidth + this.gutter) * 1
34457             });
34458             
34459             return pos;
34460             
34461         }
34462         
34463         pos.push({
34464             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34465             y : minY
34466         });
34467
34468         pos.push({
34469             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34470             y : minY + (this.unitWidth + this.gutter) * 2
34471         });
34472
34473         pos.push({
34474             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34475             y : minY + (this.unitWidth + this.gutter) * 2
34476         });
34477             
34478         return pos;
34479         
34480     },
34481     
34482     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34483     {
34484         var pos = [];
34485         
34486         if(box[0].size == 'xs'){
34487             
34488             pos.push({
34489                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34490                 y : minY
34491             });
34492
34493             pos.push({
34494                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34495                 y : minY
34496             });
34497             
34498             pos.push({
34499                 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),
34500                 y : minY
34501             });
34502             
34503             pos.push({
34504                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34505                 y : minY + (this.unitWidth + this.gutter) * 1
34506             });
34507             
34508             return pos;
34509             
34510         }
34511         
34512         pos.push({
34513             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34514             y : minY
34515         });
34516         
34517         pos.push({
34518             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34519             y : minY + (this.unitWidth + this.gutter) * 2
34520         });
34521         
34522         pos.push({
34523             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34524             y : minY + (this.unitWidth + this.gutter) * 2
34525         });
34526         
34527         pos.push({
34528             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),
34529             y : minY + (this.unitWidth + this.gutter) * 2
34530         });
34531
34532         return pos;
34533         
34534     },
34535     
34536     /**
34537     * remove a Masonry Brick
34538     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34539     */
34540     removeBrick : function(brick_id)
34541     {
34542         if (!brick_id) {
34543             return;
34544         }
34545         
34546         for (var i = 0; i<this.bricks.length; i++) {
34547             if (this.bricks[i].id == brick_id) {
34548                 this.bricks.splice(i,1);
34549                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34550                 this.initial();
34551             }
34552         }
34553     },
34554     
34555     /**
34556     * adds a Masonry Brick
34557     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34558     */
34559     addBrick : function(cfg)
34560     {
34561         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34562         //this.register(cn);
34563         cn.parentId = this.id;
34564         cn.render(this.el);
34565         return cn;
34566     },
34567     
34568     /**
34569     * register a Masonry Brick
34570     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34571     */
34572     
34573     register : function(brick)
34574     {
34575         this.bricks.push(brick);
34576         brick.masonryId = this.id;
34577     },
34578     
34579     /**
34580     * clear all the Masonry Brick
34581     */
34582     clearAll : function()
34583     {
34584         this.bricks = [];
34585         //this.getChildContainer().dom.innerHTML = "";
34586         this.el.dom.innerHTML = '';
34587     },
34588     
34589     getSelected : function()
34590     {
34591         if (!this.selectedBrick) {
34592             return false;
34593         }
34594         
34595         return this.selectedBrick;
34596     }
34597 });
34598
34599 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34600     
34601     groups: {},
34602      /**
34603     * register a Masonry Layout
34604     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34605     */
34606     
34607     register : function(layout)
34608     {
34609         this.groups[layout.id] = layout;
34610     },
34611     /**
34612     * fetch a  Masonry Layout based on the masonry layout ID
34613     * @param {string} the masonry layout to add
34614     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34615     */
34616     
34617     get: function(layout_id) {
34618         if (typeof(this.groups[layout_id]) == 'undefined') {
34619             return false;
34620         }
34621         return this.groups[layout_id] ;
34622     }
34623     
34624     
34625     
34626 });
34627
34628  
34629
34630  /**
34631  *
34632  * This is based on 
34633  * http://masonry.desandro.com
34634  *
34635  * The idea is to render all the bricks based on vertical width...
34636  *
34637  * The original code extends 'outlayer' - we might need to use that....
34638  * 
34639  */
34640
34641
34642 /**
34643  * @class Roo.bootstrap.LayoutMasonryAuto
34644  * @extends Roo.bootstrap.Component
34645  * Bootstrap Layout Masonry class
34646  * 
34647  * @constructor
34648  * Create a new Element
34649  * @param {Object} config The config object
34650  */
34651
34652 Roo.bootstrap.LayoutMasonryAuto = function(config){
34653     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34654 };
34655
34656 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34657     
34658       /**
34659      * @cfg {Boolean} isFitWidth  - resize the width..
34660      */   
34661     isFitWidth : false,  // options..
34662     /**
34663      * @cfg {Boolean} isOriginLeft = left align?
34664      */   
34665     isOriginLeft : true,
34666     /**
34667      * @cfg {Boolean} isOriginTop = top align?
34668      */   
34669     isOriginTop : false,
34670     /**
34671      * @cfg {Boolean} isLayoutInstant = no animation?
34672      */   
34673     isLayoutInstant : false, // needed?
34674     /**
34675      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34676      */   
34677     isResizingContainer : true,
34678     /**
34679      * @cfg {Number} columnWidth  width of the columns 
34680      */   
34681     
34682     columnWidth : 0,
34683     
34684     /**
34685      * @cfg {Number} maxCols maximum number of columns
34686      */   
34687     
34688     maxCols: 0,
34689     /**
34690      * @cfg {Number} padHeight padding below box..
34691      */   
34692     
34693     padHeight : 10, 
34694     
34695     /**
34696      * @cfg {Boolean} isAutoInitial defalut true
34697      */   
34698     
34699     isAutoInitial : true, 
34700     
34701     // private?
34702     gutter : 0,
34703     
34704     containerWidth: 0,
34705     initialColumnWidth : 0,
34706     currentSize : null,
34707     
34708     colYs : null, // array.
34709     maxY : 0,
34710     padWidth: 10,
34711     
34712     
34713     tag: 'div',
34714     cls: '',
34715     bricks: null, //CompositeElement
34716     cols : 0, // array?
34717     // element : null, // wrapped now this.el
34718     _isLayoutInited : null, 
34719     
34720     
34721     getAutoCreate : function(){
34722         
34723         var cfg = {
34724             tag: this.tag,
34725             cls: 'blog-masonary-wrapper ' + this.cls,
34726             cn : {
34727                 cls : 'mas-boxes masonary'
34728             }
34729         };
34730         
34731         return cfg;
34732     },
34733     
34734     getChildContainer: function( )
34735     {
34736         if (this.boxesEl) {
34737             return this.boxesEl;
34738         }
34739         
34740         this.boxesEl = this.el.select('.mas-boxes').first();
34741         
34742         return this.boxesEl;
34743     },
34744     
34745     
34746     initEvents : function()
34747     {
34748         var _this = this;
34749         
34750         if(this.isAutoInitial){
34751             Roo.log('hook children rendered');
34752             this.on('childrenrendered', function() {
34753                 Roo.log('children rendered');
34754                 _this.initial();
34755             } ,this);
34756         }
34757         
34758     },
34759     
34760     initial : function()
34761     {
34762         this.reloadItems();
34763
34764         this.currentSize = this.el.getBox(true);
34765
34766         /// was window resize... - let's see if this works..
34767         Roo.EventManager.onWindowResize(this.resize, this); 
34768
34769         if(!this.isAutoInitial){
34770             this.layout();
34771             return;
34772         }
34773         
34774         this.layout.defer(500,this);
34775     },
34776     
34777     reloadItems: function()
34778     {
34779         this.bricks = this.el.select('.masonry-brick', true);
34780         
34781         this.bricks.each(function(b) {
34782             //Roo.log(b.getSize());
34783             if (!b.attr('originalwidth')) {
34784                 b.attr('originalwidth',  b.getSize().width);
34785             }
34786             
34787         });
34788         
34789         Roo.log(this.bricks.elements.length);
34790     },
34791     
34792     resize : function()
34793     {
34794         Roo.log('resize');
34795         var cs = this.el.getBox(true);
34796         
34797         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34798             Roo.log("no change in with or X");
34799             return;
34800         }
34801         this.currentSize = cs;
34802         this.layout();
34803     },
34804     
34805     layout : function()
34806     {
34807          Roo.log('layout');
34808         this._resetLayout();
34809         //this._manageStamps();
34810       
34811         // don't animate first layout
34812         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34813         this.layoutItems( isInstant );
34814       
34815         // flag for initalized
34816         this._isLayoutInited = true;
34817     },
34818     
34819     layoutItems : function( isInstant )
34820     {
34821         //var items = this._getItemsForLayout( this.items );
34822         // original code supports filtering layout items.. we just ignore it..
34823         
34824         this._layoutItems( this.bricks , isInstant );
34825       
34826         this._postLayout();
34827     },
34828     _layoutItems : function ( items , isInstant)
34829     {
34830        //this.fireEvent( 'layout', this, items );
34831     
34832
34833         if ( !items || !items.elements.length ) {
34834           // no items, emit event with empty array
34835             return;
34836         }
34837
34838         var queue = [];
34839         items.each(function(item) {
34840             Roo.log("layout item");
34841             Roo.log(item);
34842             // get x/y object from method
34843             var position = this._getItemLayoutPosition( item );
34844             // enqueue
34845             position.item = item;
34846             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34847             queue.push( position );
34848         }, this);
34849       
34850         this._processLayoutQueue( queue );
34851     },
34852     /** Sets position of item in DOM
34853     * @param {Element} item
34854     * @param {Number} x - horizontal position
34855     * @param {Number} y - vertical position
34856     * @param {Boolean} isInstant - disables transitions
34857     */
34858     _processLayoutQueue : function( queue )
34859     {
34860         for ( var i=0, len = queue.length; i < len; i++ ) {
34861             var obj = queue[i];
34862             obj.item.position('absolute');
34863             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34864         }
34865     },
34866       
34867     
34868     /**
34869     * Any logic you want to do after each layout,
34870     * i.e. size the container
34871     */
34872     _postLayout : function()
34873     {
34874         this.resizeContainer();
34875     },
34876     
34877     resizeContainer : function()
34878     {
34879         if ( !this.isResizingContainer ) {
34880             return;
34881         }
34882         var size = this._getContainerSize();
34883         if ( size ) {
34884             this.el.setSize(size.width,size.height);
34885             this.boxesEl.setSize(size.width,size.height);
34886         }
34887     },
34888     
34889     
34890     
34891     _resetLayout : function()
34892     {
34893         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34894         this.colWidth = this.el.getWidth();
34895         //this.gutter = this.el.getWidth(); 
34896         
34897         this.measureColumns();
34898
34899         // reset column Y
34900         var i = this.cols;
34901         this.colYs = [];
34902         while (i--) {
34903             this.colYs.push( 0 );
34904         }
34905     
34906         this.maxY = 0;
34907     },
34908
34909     measureColumns : function()
34910     {
34911         this.getContainerWidth();
34912       // if columnWidth is 0, default to outerWidth of first item
34913         if ( !this.columnWidth ) {
34914             var firstItem = this.bricks.first();
34915             Roo.log(firstItem);
34916             this.columnWidth  = this.containerWidth;
34917             if (firstItem && firstItem.attr('originalwidth') ) {
34918                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34919             }
34920             // columnWidth fall back to item of first element
34921             Roo.log("set column width?");
34922                         this.initialColumnWidth = this.columnWidth  ;
34923
34924             // if first elem has no width, default to size of container
34925             
34926         }
34927         
34928         
34929         if (this.initialColumnWidth) {
34930             this.columnWidth = this.initialColumnWidth;
34931         }
34932         
34933         
34934             
34935         // column width is fixed at the top - however if container width get's smaller we should
34936         // reduce it...
34937         
34938         // this bit calcs how man columns..
34939             
34940         var columnWidth = this.columnWidth += this.gutter;
34941       
34942         // calculate columns
34943         var containerWidth = this.containerWidth + this.gutter;
34944         
34945         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34946         // fix rounding errors, typically with gutters
34947         var excess = columnWidth - containerWidth % columnWidth;
34948         
34949         
34950         // if overshoot is less than a pixel, round up, otherwise floor it
34951         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34952         cols = Math[ mathMethod ]( cols );
34953         this.cols = Math.max( cols, 1 );
34954         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34955         
34956          // padding positioning..
34957         var totalColWidth = this.cols * this.columnWidth;
34958         var padavail = this.containerWidth - totalColWidth;
34959         // so for 2 columns - we need 3 'pads'
34960         
34961         var padNeeded = (1+this.cols) * this.padWidth;
34962         
34963         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34964         
34965         this.columnWidth += padExtra
34966         //this.padWidth = Math.floor(padavail /  ( this.cols));
34967         
34968         // adjust colum width so that padding is fixed??
34969         
34970         // we have 3 columns ... total = width * 3
34971         // we have X left over... that should be used by 
34972         
34973         //if (this.expandC) {
34974             
34975         //}
34976         
34977         
34978         
34979     },
34980     
34981     getContainerWidth : function()
34982     {
34983        /* // container is parent if fit width
34984         var container = this.isFitWidth ? this.element.parentNode : this.element;
34985         // check that this.size and size are there
34986         // IE8 triggers resize on body size change, so they might not be
34987         
34988         var size = getSize( container );  //FIXME
34989         this.containerWidth = size && size.innerWidth; //FIXME
34990         */
34991          
34992         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34993         
34994     },
34995     
34996     _getItemLayoutPosition : function( item )  // what is item?
34997     {
34998         // we resize the item to our columnWidth..
34999       
35000         item.setWidth(this.columnWidth);
35001         item.autoBoxAdjust  = false;
35002         
35003         var sz = item.getSize();
35004  
35005         // how many columns does this brick span
35006         var remainder = this.containerWidth % this.columnWidth;
35007         
35008         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35009         // round if off by 1 pixel, otherwise use ceil
35010         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35011         colSpan = Math.min( colSpan, this.cols );
35012         
35013         // normally this should be '1' as we dont' currently allow multi width columns..
35014         
35015         var colGroup = this._getColGroup( colSpan );
35016         // get the minimum Y value from the columns
35017         var minimumY = Math.min.apply( Math, colGroup );
35018         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35019         
35020         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35021          
35022         // position the brick
35023         var position = {
35024             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35025             y: this.currentSize.y + minimumY + this.padHeight
35026         };
35027         
35028         Roo.log(position);
35029         // apply setHeight to necessary columns
35030         var setHeight = minimumY + sz.height + this.padHeight;
35031         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35032         
35033         var setSpan = this.cols + 1 - colGroup.length;
35034         for ( var i = 0; i < setSpan; i++ ) {
35035           this.colYs[ shortColIndex + i ] = setHeight ;
35036         }
35037       
35038         return position;
35039     },
35040     
35041     /**
35042      * @param {Number} colSpan - number of columns the element spans
35043      * @returns {Array} colGroup
35044      */
35045     _getColGroup : function( colSpan )
35046     {
35047         if ( colSpan < 2 ) {
35048           // if brick spans only one column, use all the column Ys
35049           return this.colYs;
35050         }
35051       
35052         var colGroup = [];
35053         // how many different places could this brick fit horizontally
35054         var groupCount = this.cols + 1 - colSpan;
35055         // for each group potential horizontal position
35056         for ( var i = 0; i < groupCount; i++ ) {
35057           // make an array of colY values for that one group
35058           var groupColYs = this.colYs.slice( i, i + colSpan );
35059           // and get the max value of the array
35060           colGroup[i] = Math.max.apply( Math, groupColYs );
35061         }
35062         return colGroup;
35063     },
35064     /*
35065     _manageStamp : function( stamp )
35066     {
35067         var stampSize =  stamp.getSize();
35068         var offset = stamp.getBox();
35069         // get the columns that this stamp affects
35070         var firstX = this.isOriginLeft ? offset.x : offset.right;
35071         var lastX = firstX + stampSize.width;
35072         var firstCol = Math.floor( firstX / this.columnWidth );
35073         firstCol = Math.max( 0, firstCol );
35074         
35075         var lastCol = Math.floor( lastX / this.columnWidth );
35076         // lastCol should not go over if multiple of columnWidth #425
35077         lastCol -= lastX % this.columnWidth ? 0 : 1;
35078         lastCol = Math.min( this.cols - 1, lastCol );
35079         
35080         // set colYs to bottom of the stamp
35081         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35082             stampSize.height;
35083             
35084         for ( var i = firstCol; i <= lastCol; i++ ) {
35085           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35086         }
35087     },
35088     */
35089     
35090     _getContainerSize : function()
35091     {
35092         this.maxY = Math.max.apply( Math, this.colYs );
35093         var size = {
35094             height: this.maxY
35095         };
35096       
35097         if ( this.isFitWidth ) {
35098             size.width = this._getContainerFitWidth();
35099         }
35100       
35101         return size;
35102     },
35103     
35104     _getContainerFitWidth : function()
35105     {
35106         var unusedCols = 0;
35107         // count unused columns
35108         var i = this.cols;
35109         while ( --i ) {
35110           if ( this.colYs[i] !== 0 ) {
35111             break;
35112           }
35113           unusedCols++;
35114         }
35115         // fit container to columns that have been used
35116         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35117     },
35118     
35119     needsResizeLayout : function()
35120     {
35121         var previousWidth = this.containerWidth;
35122         this.getContainerWidth();
35123         return previousWidth !== this.containerWidth;
35124     }
35125  
35126 });
35127
35128  
35129
35130  /*
35131  * - LGPL
35132  *
35133  * element
35134  * 
35135  */
35136
35137 /**
35138  * @class Roo.bootstrap.MasonryBrick
35139  * @extends Roo.bootstrap.Component
35140  * Bootstrap MasonryBrick class
35141  * 
35142  * @constructor
35143  * Create a new MasonryBrick
35144  * @param {Object} config The config object
35145  */
35146
35147 Roo.bootstrap.MasonryBrick = function(config){
35148     
35149     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35150     
35151     Roo.bootstrap.MasonryBrick.register(this);
35152     
35153     this.addEvents({
35154         // raw events
35155         /**
35156          * @event click
35157          * When a MasonryBrick is clcik
35158          * @param {Roo.bootstrap.MasonryBrick} this
35159          * @param {Roo.EventObject} e
35160          */
35161         "click" : true
35162     });
35163 };
35164
35165 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35166     
35167     /**
35168      * @cfg {String} title
35169      */   
35170     title : '',
35171     /**
35172      * @cfg {String} html
35173      */   
35174     html : '',
35175     /**
35176      * @cfg {String} bgimage
35177      */   
35178     bgimage : '',
35179     /**
35180      * @cfg {String} videourl
35181      */   
35182     videourl : '',
35183     /**
35184      * @cfg {String} cls
35185      */   
35186     cls : '',
35187     /**
35188      * @cfg {String} href
35189      */   
35190     href : '',
35191     /**
35192      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35193      */   
35194     size : 'xs',
35195     
35196     /**
35197      * @cfg {String} placetitle (center|bottom)
35198      */   
35199     placetitle : '',
35200     
35201     /**
35202      * @cfg {Boolean} isFitContainer defalut true
35203      */   
35204     isFitContainer : true, 
35205     
35206     /**
35207      * @cfg {Boolean} preventDefault defalut false
35208      */   
35209     preventDefault : false, 
35210     
35211     /**
35212      * @cfg {Boolean} inverse defalut false
35213      */   
35214     maskInverse : false, 
35215     
35216     getAutoCreate : function()
35217     {
35218         if(!this.isFitContainer){
35219             return this.getSplitAutoCreate();
35220         }
35221         
35222         var cls = 'masonry-brick masonry-brick-full';
35223         
35224         if(this.href.length){
35225             cls += ' masonry-brick-link';
35226         }
35227         
35228         if(this.bgimage.length){
35229             cls += ' masonry-brick-image';
35230         }
35231         
35232         if(this.maskInverse){
35233             cls += ' mask-inverse';
35234         }
35235         
35236         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35237             cls += ' enable-mask';
35238         }
35239         
35240         if(this.size){
35241             cls += ' masonry-' + this.size + '-brick';
35242         }
35243         
35244         if(this.placetitle.length){
35245             
35246             switch (this.placetitle) {
35247                 case 'center' :
35248                     cls += ' masonry-center-title';
35249                     break;
35250                 case 'bottom' :
35251                     cls += ' masonry-bottom-title';
35252                     break;
35253                 default:
35254                     break;
35255             }
35256             
35257         } else {
35258             if(!this.html.length && !this.bgimage.length){
35259                 cls += ' masonry-center-title';
35260             }
35261
35262             if(!this.html.length && this.bgimage.length){
35263                 cls += ' masonry-bottom-title';
35264             }
35265         }
35266         
35267         if(this.cls){
35268             cls += ' ' + this.cls;
35269         }
35270         
35271         var cfg = {
35272             tag: (this.href.length) ? 'a' : 'div',
35273             cls: cls,
35274             cn: [
35275                 {
35276                     tag: 'div',
35277                     cls: 'masonry-brick-mask'
35278                 },
35279                 {
35280                     tag: 'div',
35281                     cls: 'masonry-brick-paragraph',
35282                     cn: []
35283                 }
35284             ]
35285         };
35286         
35287         if(this.href.length){
35288             cfg.href = this.href;
35289         }
35290         
35291         var cn = cfg.cn[1].cn;
35292         
35293         if(this.title.length){
35294             cn.push({
35295                 tag: 'h4',
35296                 cls: 'masonry-brick-title',
35297                 html: this.title
35298             });
35299         }
35300         
35301         if(this.html.length){
35302             cn.push({
35303                 tag: 'p',
35304                 cls: 'masonry-brick-text',
35305                 html: this.html
35306             });
35307         }
35308         
35309         if (!this.title.length && !this.html.length) {
35310             cfg.cn[1].cls += ' hide';
35311         }
35312         
35313         if(this.bgimage.length){
35314             cfg.cn.push({
35315                 tag: 'img',
35316                 cls: 'masonry-brick-image-view',
35317                 src: this.bgimage
35318             });
35319         }
35320         
35321         if(this.videourl.length){
35322             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35323             // youtube support only?
35324             cfg.cn.push({
35325                 tag: 'iframe',
35326                 cls: 'masonry-brick-image-view',
35327                 src: vurl,
35328                 frameborder : 0,
35329                 allowfullscreen : true
35330             });
35331         }
35332         
35333         return cfg;
35334         
35335     },
35336     
35337     getSplitAutoCreate : function()
35338     {
35339         var cls = 'masonry-brick masonry-brick-split';
35340         
35341         if(this.href.length){
35342             cls += ' masonry-brick-link';
35343         }
35344         
35345         if(this.bgimage.length){
35346             cls += ' masonry-brick-image';
35347         }
35348         
35349         if(this.size){
35350             cls += ' masonry-' + this.size + '-brick';
35351         }
35352         
35353         switch (this.placetitle) {
35354             case 'center' :
35355                 cls += ' masonry-center-title';
35356                 break;
35357             case 'bottom' :
35358                 cls += ' masonry-bottom-title';
35359                 break;
35360             default:
35361                 if(!this.bgimage.length){
35362                     cls += ' masonry-center-title';
35363                 }
35364
35365                 if(this.bgimage.length){
35366                     cls += ' masonry-bottom-title';
35367                 }
35368                 break;
35369         }
35370         
35371         if(this.cls){
35372             cls += ' ' + this.cls;
35373         }
35374         
35375         var cfg = {
35376             tag: (this.href.length) ? 'a' : 'div',
35377             cls: cls,
35378             cn: [
35379                 {
35380                     tag: 'div',
35381                     cls: 'masonry-brick-split-head',
35382                     cn: [
35383                         {
35384                             tag: 'div',
35385                             cls: 'masonry-brick-paragraph',
35386                             cn: []
35387                         }
35388                     ]
35389                 },
35390                 {
35391                     tag: 'div',
35392                     cls: 'masonry-brick-split-body',
35393                     cn: []
35394                 }
35395             ]
35396         };
35397         
35398         if(this.href.length){
35399             cfg.href = this.href;
35400         }
35401         
35402         if(this.title.length){
35403             cfg.cn[0].cn[0].cn.push({
35404                 tag: 'h4',
35405                 cls: 'masonry-brick-title',
35406                 html: this.title
35407             });
35408         }
35409         
35410         if(this.html.length){
35411             cfg.cn[1].cn.push({
35412                 tag: 'p',
35413                 cls: 'masonry-brick-text',
35414                 html: this.html
35415             });
35416         }
35417
35418         if(this.bgimage.length){
35419             cfg.cn[0].cn.push({
35420                 tag: 'img',
35421                 cls: 'masonry-brick-image-view',
35422                 src: this.bgimage
35423             });
35424         }
35425         
35426         if(this.videourl.length){
35427             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35428             // youtube support only?
35429             cfg.cn[0].cn.cn.push({
35430                 tag: 'iframe',
35431                 cls: 'masonry-brick-image-view',
35432                 src: vurl,
35433                 frameborder : 0,
35434                 allowfullscreen : true
35435             });
35436         }
35437         
35438         return cfg;
35439     },
35440     
35441     initEvents: function() 
35442     {
35443         switch (this.size) {
35444             case 'xs' :
35445                 this.x = 1;
35446                 this.y = 1;
35447                 break;
35448             case 'sm' :
35449                 this.x = 2;
35450                 this.y = 2;
35451                 break;
35452             case 'md' :
35453             case 'md-left' :
35454             case 'md-right' :
35455                 this.x = 3;
35456                 this.y = 3;
35457                 break;
35458             case 'tall' :
35459                 this.x = 2;
35460                 this.y = 3;
35461                 break;
35462             case 'wide' :
35463                 this.x = 3;
35464                 this.y = 2;
35465                 break;
35466             case 'wide-thin' :
35467                 this.x = 3;
35468                 this.y = 1;
35469                 break;
35470                         
35471             default :
35472                 break;
35473         }
35474         
35475         if(Roo.isTouch){
35476             this.el.on('touchstart', this.onTouchStart, this);
35477             this.el.on('touchmove', this.onTouchMove, this);
35478             this.el.on('touchend', this.onTouchEnd, this);
35479             this.el.on('contextmenu', this.onContextMenu, this);
35480         } else {
35481             this.el.on('mouseenter'  ,this.enter, this);
35482             this.el.on('mouseleave', this.leave, this);
35483             this.el.on('click', this.onClick, this);
35484         }
35485         
35486         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35487             this.parent().bricks.push(this);   
35488         }
35489         
35490     },
35491     
35492     onClick: function(e, el)
35493     {
35494         var time = this.endTimer - this.startTimer;
35495         // Roo.log(e.preventDefault());
35496         if(Roo.isTouch){
35497             if(time > 1000){
35498                 e.preventDefault();
35499                 return;
35500             }
35501         }
35502         
35503         if(!this.preventDefault){
35504             return;
35505         }
35506         
35507         e.preventDefault();
35508         
35509         if (this.activeClass != '') {
35510             this.selectBrick();
35511         }
35512         
35513         this.fireEvent('click', this, e);
35514     },
35515     
35516     enter: function(e, el)
35517     {
35518         e.preventDefault();
35519         
35520         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35521             return;
35522         }
35523         
35524         if(this.bgimage.length && this.html.length){
35525             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35526         }
35527     },
35528     
35529     leave: function(e, el)
35530     {
35531         e.preventDefault();
35532         
35533         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35534             return;
35535         }
35536         
35537         if(this.bgimage.length && this.html.length){
35538             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35539         }
35540     },
35541     
35542     onTouchStart: function(e, el)
35543     {
35544 //        e.preventDefault();
35545         
35546         this.touchmoved = false;
35547         
35548         if(!this.isFitContainer){
35549             return;
35550         }
35551         
35552         if(!this.bgimage.length || !this.html.length){
35553             return;
35554         }
35555         
35556         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35557         
35558         this.timer = new Date().getTime();
35559         
35560     },
35561     
35562     onTouchMove: function(e, el)
35563     {
35564         this.touchmoved = true;
35565     },
35566     
35567     onContextMenu : function(e,el)
35568     {
35569         e.preventDefault();
35570         e.stopPropagation();
35571         return false;
35572     },
35573     
35574     onTouchEnd: function(e, el)
35575     {
35576 //        e.preventDefault();
35577         
35578         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35579         
35580             this.leave(e,el);
35581             
35582             return;
35583         }
35584         
35585         if(!this.bgimage.length || !this.html.length){
35586             
35587             if(this.href.length){
35588                 window.location.href = this.href;
35589             }
35590             
35591             return;
35592         }
35593         
35594         if(!this.isFitContainer){
35595             return;
35596         }
35597         
35598         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35599         
35600         window.location.href = this.href;
35601     },
35602     
35603     //selection on single brick only
35604     selectBrick : function() {
35605         
35606         if (!this.parentId) {
35607             return;
35608         }
35609         
35610         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35611         var index = m.selectedBrick.indexOf(this.id);
35612         
35613         if ( index > -1) {
35614             m.selectedBrick.splice(index,1);
35615             this.el.removeClass(this.activeClass);
35616             return;
35617         }
35618         
35619         for(var i = 0; i < m.selectedBrick.length; i++) {
35620             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35621             b.el.removeClass(b.activeClass);
35622         }
35623         
35624         m.selectedBrick = [];
35625         
35626         m.selectedBrick.push(this.id);
35627         this.el.addClass(this.activeClass);
35628         return;
35629     },
35630     
35631     isSelected : function(){
35632         return this.el.hasClass(this.activeClass);
35633         
35634     }
35635 });
35636
35637 Roo.apply(Roo.bootstrap.MasonryBrick, {
35638     
35639     //groups: {},
35640     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35641      /**
35642     * register a Masonry Brick
35643     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35644     */
35645     
35646     register : function(brick)
35647     {
35648         //this.groups[brick.id] = brick;
35649         this.groups.add(brick.id, brick);
35650     },
35651     /**
35652     * fetch a  masonry brick based on the masonry brick ID
35653     * @param {string} the masonry brick to add
35654     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35655     */
35656     
35657     get: function(brick_id) 
35658     {
35659         // if (typeof(this.groups[brick_id]) == 'undefined') {
35660         //     return false;
35661         // }
35662         // return this.groups[brick_id] ;
35663         
35664         if(this.groups.key(brick_id)) {
35665             return this.groups.key(brick_id);
35666         }
35667         
35668         return false;
35669     }
35670     
35671     
35672     
35673 });
35674
35675  /*
35676  * - LGPL
35677  *
35678  * element
35679  * 
35680  */
35681
35682 /**
35683  * @class Roo.bootstrap.Brick
35684  * @extends Roo.bootstrap.Component
35685  * Bootstrap Brick class
35686  * 
35687  * @constructor
35688  * Create a new Brick
35689  * @param {Object} config The config object
35690  */
35691
35692 Roo.bootstrap.Brick = function(config){
35693     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35694     
35695     this.addEvents({
35696         // raw events
35697         /**
35698          * @event click
35699          * When a Brick is click
35700          * @param {Roo.bootstrap.Brick} this
35701          * @param {Roo.EventObject} e
35702          */
35703         "click" : true
35704     });
35705 };
35706
35707 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35708     
35709     /**
35710      * @cfg {String} title
35711      */   
35712     title : '',
35713     /**
35714      * @cfg {String} html
35715      */   
35716     html : '',
35717     /**
35718      * @cfg {String} bgimage
35719      */   
35720     bgimage : '',
35721     /**
35722      * @cfg {String} cls
35723      */   
35724     cls : '',
35725     /**
35726      * @cfg {String} href
35727      */   
35728     href : '',
35729     /**
35730      * @cfg {String} video
35731      */   
35732     video : '',
35733     /**
35734      * @cfg {Boolean} square
35735      */   
35736     square : true,
35737     
35738     getAutoCreate : function()
35739     {
35740         var cls = 'roo-brick';
35741         
35742         if(this.href.length){
35743             cls += ' roo-brick-link';
35744         }
35745         
35746         if(this.bgimage.length){
35747             cls += ' roo-brick-image';
35748         }
35749         
35750         if(!this.html.length && !this.bgimage.length){
35751             cls += ' roo-brick-center-title';
35752         }
35753         
35754         if(!this.html.length && this.bgimage.length){
35755             cls += ' roo-brick-bottom-title';
35756         }
35757         
35758         if(this.cls){
35759             cls += ' ' + this.cls;
35760         }
35761         
35762         var cfg = {
35763             tag: (this.href.length) ? 'a' : 'div',
35764             cls: cls,
35765             cn: [
35766                 {
35767                     tag: 'div',
35768                     cls: 'roo-brick-paragraph',
35769                     cn: []
35770                 }
35771             ]
35772         };
35773         
35774         if(this.href.length){
35775             cfg.href = this.href;
35776         }
35777         
35778         var cn = cfg.cn[0].cn;
35779         
35780         if(this.title.length){
35781             cn.push({
35782                 tag: 'h4',
35783                 cls: 'roo-brick-title',
35784                 html: this.title
35785             });
35786         }
35787         
35788         if(this.html.length){
35789             cn.push({
35790                 tag: 'p',
35791                 cls: 'roo-brick-text',
35792                 html: this.html
35793             });
35794         } else {
35795             cn.cls += ' hide';
35796         }
35797         
35798         if(this.bgimage.length){
35799             cfg.cn.push({
35800                 tag: 'img',
35801                 cls: 'roo-brick-image-view',
35802                 src: this.bgimage
35803             });
35804         }
35805         
35806         return cfg;
35807     },
35808     
35809     initEvents: function() 
35810     {
35811         if(this.title.length || this.html.length){
35812             this.el.on('mouseenter'  ,this.enter, this);
35813             this.el.on('mouseleave', this.leave, this);
35814         }
35815         
35816         Roo.EventManager.onWindowResize(this.resize, this); 
35817         
35818         if(this.bgimage.length){
35819             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35820             this.imageEl.on('load', this.onImageLoad, this);
35821             return;
35822         }
35823         
35824         this.resize();
35825     },
35826     
35827     onImageLoad : function()
35828     {
35829         this.resize();
35830     },
35831     
35832     resize : function()
35833     {
35834         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35835         
35836         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35837         
35838         if(this.bgimage.length){
35839             var image = this.el.select('.roo-brick-image-view', true).first();
35840             
35841             image.setWidth(paragraph.getWidth());
35842             
35843             if(this.square){
35844                 image.setHeight(paragraph.getWidth());
35845             }
35846             
35847             this.el.setHeight(image.getHeight());
35848             paragraph.setHeight(image.getHeight());
35849             
35850         }
35851         
35852     },
35853     
35854     enter: function(e, el)
35855     {
35856         e.preventDefault();
35857         
35858         if(this.bgimage.length){
35859             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35860             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35861         }
35862     },
35863     
35864     leave: function(e, el)
35865     {
35866         e.preventDefault();
35867         
35868         if(this.bgimage.length){
35869             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35870             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35871         }
35872     }
35873     
35874 });
35875
35876  
35877
35878  /*
35879  * - LGPL
35880  *
35881  * Number field 
35882  */
35883
35884 /**
35885  * @class Roo.bootstrap.NumberField
35886  * @extends Roo.bootstrap.Input
35887  * Bootstrap NumberField class
35888  * 
35889  * 
35890  * 
35891  * 
35892  * @constructor
35893  * Create a new NumberField
35894  * @param {Object} config The config object
35895  */
35896
35897 Roo.bootstrap.NumberField = function(config){
35898     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35899 };
35900
35901 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35902     
35903     /**
35904      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35905      */
35906     allowDecimals : true,
35907     /**
35908      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35909      */
35910     decimalSeparator : ".",
35911     /**
35912      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35913      */
35914     decimalPrecision : 2,
35915     /**
35916      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35917      */
35918     allowNegative : true,
35919     
35920     /**
35921      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35922      */
35923     allowZero: true,
35924     /**
35925      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35926      */
35927     minValue : Number.NEGATIVE_INFINITY,
35928     /**
35929      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35930      */
35931     maxValue : Number.MAX_VALUE,
35932     /**
35933      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35934      */
35935     minText : "The minimum value for this field is {0}",
35936     /**
35937      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35938      */
35939     maxText : "The maximum value for this field is {0}",
35940     /**
35941      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35942      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35943      */
35944     nanText : "{0} is not a valid number",
35945     /**
35946      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35947      */
35948     thousandsDelimiter : false,
35949     /**
35950      * @cfg {String} valueAlign alignment of value
35951      */
35952     valueAlign : "left",
35953
35954     getAutoCreate : function()
35955     {
35956         var hiddenInput = {
35957             tag: 'input',
35958             type: 'hidden',
35959             id: Roo.id(),
35960             cls: 'hidden-number-input'
35961         };
35962         
35963         if (this.name) {
35964             hiddenInput.name = this.name;
35965         }
35966         
35967         this.name = '';
35968         
35969         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35970         
35971         this.name = hiddenInput.name;
35972         
35973         if(cfg.cn.length > 0) {
35974             cfg.cn.push(hiddenInput);
35975         }
35976         
35977         return cfg;
35978     },
35979
35980     // private
35981     initEvents : function()
35982     {   
35983         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35984         
35985         var allowed = "0123456789";
35986         
35987         if(this.allowDecimals){
35988             allowed += this.decimalSeparator;
35989         }
35990         
35991         if(this.allowNegative){
35992             allowed += "-";
35993         }
35994         
35995         if(this.thousandsDelimiter) {
35996             allowed += ",";
35997         }
35998         
35999         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36000         
36001         var keyPress = function(e){
36002             
36003             var k = e.getKey();
36004             
36005             var c = e.getCharCode();
36006             
36007             if(
36008                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36009                     allowed.indexOf(String.fromCharCode(c)) === -1
36010             ){
36011                 e.stopEvent();
36012                 return;
36013             }
36014             
36015             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36016                 return;
36017             }
36018             
36019             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36020                 e.stopEvent();
36021             }
36022         };
36023         
36024         this.el.on("keypress", keyPress, this);
36025     },
36026     
36027     validateValue : function(value)
36028     {
36029         
36030         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36031             return false;
36032         }
36033         
36034         var num = this.parseValue(value);
36035         
36036         if(isNaN(num)){
36037             this.markInvalid(String.format(this.nanText, value));
36038             return false;
36039         }
36040         
36041         if(num < this.minValue){
36042             this.markInvalid(String.format(this.minText, this.minValue));
36043             return false;
36044         }
36045         
36046         if(num > this.maxValue){
36047             this.markInvalid(String.format(this.maxText, this.maxValue));
36048             return false;
36049         }
36050         
36051         return true;
36052     },
36053
36054     getValue : function()
36055     {
36056         var v = this.hiddenEl().getValue();
36057         
36058         return this.fixPrecision(this.parseValue(v));
36059     },
36060
36061     parseValue : function(value)
36062     {
36063         if(this.thousandsDelimiter) {
36064             value += "";
36065             r = new RegExp(",", "g");
36066             value = value.replace(r, "");
36067         }
36068         
36069         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36070         return isNaN(value) ? '' : value;
36071     },
36072
36073     fixPrecision : function(value)
36074     {
36075         if(this.thousandsDelimiter) {
36076             value += "";
36077             r = new RegExp(",", "g");
36078             value = value.replace(r, "");
36079         }
36080         
36081         var nan = isNaN(value);
36082         
36083         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36084             return nan ? '' : value;
36085         }
36086         return parseFloat(value).toFixed(this.decimalPrecision);
36087     },
36088
36089     setValue : function(v)
36090     {
36091         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36092         
36093         this.value = v;
36094         
36095         if(this.rendered){
36096             
36097             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36098             
36099             this.inputEl().dom.value = (v == '') ? '' :
36100                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36101             
36102             if(!this.allowZero && v === '0') {
36103                 this.hiddenEl().dom.value = '';
36104                 this.inputEl().dom.value = '';
36105             }
36106             
36107             this.validate();
36108         }
36109     },
36110
36111     decimalPrecisionFcn : function(v)
36112     {
36113         return Math.floor(v);
36114     },
36115
36116     beforeBlur : function()
36117     {
36118         var v = this.parseValue(this.getRawValue());
36119         
36120         if(v || v === 0 || v === ''){
36121             this.setValue(v);
36122         }
36123     },
36124     
36125     hiddenEl : function()
36126     {
36127         return this.el.select('input.hidden-number-input',true).first();
36128     }
36129     
36130 });
36131
36132  
36133
36134 /*
36135 * Licence: LGPL
36136 */
36137
36138 /**
36139  * @class Roo.bootstrap.DocumentSlider
36140  * @extends Roo.bootstrap.Component
36141  * Bootstrap DocumentSlider class
36142  * 
36143  * @constructor
36144  * Create a new DocumentViewer
36145  * @param {Object} config The config object
36146  */
36147
36148 Roo.bootstrap.DocumentSlider = function(config){
36149     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36150     
36151     this.files = [];
36152     
36153     this.addEvents({
36154         /**
36155          * @event initial
36156          * Fire after initEvent
36157          * @param {Roo.bootstrap.DocumentSlider} this
36158          */
36159         "initial" : true,
36160         /**
36161          * @event update
36162          * Fire after update
36163          * @param {Roo.bootstrap.DocumentSlider} this
36164          */
36165         "update" : true,
36166         /**
36167          * @event click
36168          * Fire after click
36169          * @param {Roo.bootstrap.DocumentSlider} this
36170          */
36171         "click" : true
36172     });
36173 };
36174
36175 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36176     
36177     files : false,
36178     
36179     indicator : 0,
36180     
36181     getAutoCreate : function()
36182     {
36183         var cfg = {
36184             tag : 'div',
36185             cls : 'roo-document-slider',
36186             cn : [
36187                 {
36188                     tag : 'div',
36189                     cls : 'roo-document-slider-header',
36190                     cn : [
36191                         {
36192                             tag : 'div',
36193                             cls : 'roo-document-slider-header-title'
36194                         }
36195                     ]
36196                 },
36197                 {
36198                     tag : 'div',
36199                     cls : 'roo-document-slider-body',
36200                     cn : [
36201                         {
36202                             tag : 'div',
36203                             cls : 'roo-document-slider-prev',
36204                             cn : [
36205                                 {
36206                                     tag : 'i',
36207                                     cls : 'fa fa-chevron-left'
36208                                 }
36209                             ]
36210                         },
36211                         {
36212                             tag : 'div',
36213                             cls : 'roo-document-slider-thumb',
36214                             cn : [
36215                                 {
36216                                     tag : 'img',
36217                                     cls : 'roo-document-slider-image'
36218                                 }
36219                             ]
36220                         },
36221                         {
36222                             tag : 'div',
36223                             cls : 'roo-document-slider-next',
36224                             cn : [
36225                                 {
36226                                     tag : 'i',
36227                                     cls : 'fa fa-chevron-right'
36228                                 }
36229                             ]
36230                         }
36231                     ]
36232                 }
36233             ]
36234         };
36235         
36236         return cfg;
36237     },
36238     
36239     initEvents : function()
36240     {
36241         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36242         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36243         
36244         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36245         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36246         
36247         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36248         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36249         
36250         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36251         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36252         
36253         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36254         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36255         
36256         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36257         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36258         
36259         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36260         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36261         
36262         this.thumbEl.on('click', this.onClick, this);
36263         
36264         this.prevIndicator.on('click', this.prev, this);
36265         
36266         this.nextIndicator.on('click', this.next, this);
36267         
36268     },
36269     
36270     initial : function()
36271     {
36272         if(this.files.length){
36273             this.indicator = 1;
36274             this.update()
36275         }
36276         
36277         this.fireEvent('initial', this);
36278     },
36279     
36280     update : function()
36281     {
36282         this.imageEl.attr('src', this.files[this.indicator - 1]);
36283         
36284         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36285         
36286         this.prevIndicator.show();
36287         
36288         if(this.indicator == 1){
36289             this.prevIndicator.hide();
36290         }
36291         
36292         this.nextIndicator.show();
36293         
36294         if(this.indicator == this.files.length){
36295             this.nextIndicator.hide();
36296         }
36297         
36298         this.thumbEl.scrollTo('top');
36299         
36300         this.fireEvent('update', this);
36301     },
36302     
36303     onClick : function(e)
36304     {
36305         e.preventDefault();
36306         
36307         this.fireEvent('click', this);
36308     },
36309     
36310     prev : function(e)
36311     {
36312         e.preventDefault();
36313         
36314         this.indicator = Math.max(1, this.indicator - 1);
36315         
36316         this.update();
36317     },
36318     
36319     next : function(e)
36320     {
36321         e.preventDefault();
36322         
36323         this.indicator = Math.min(this.files.length, this.indicator + 1);
36324         
36325         this.update();
36326     }
36327 });
36328 /*
36329  * - LGPL
36330  *
36331  * RadioSet
36332  *
36333  *
36334  */
36335
36336 /**
36337  * @class Roo.bootstrap.RadioSet
36338  * @extends Roo.bootstrap.Input
36339  * Bootstrap RadioSet class
36340  * @cfg {String} indicatorpos (left|right) default left
36341  * @cfg {Boolean} inline (true|false) inline the element (default true)
36342  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36343  * @constructor
36344  * Create a new RadioSet
36345  * @param {Object} config The config object
36346  */
36347
36348 Roo.bootstrap.RadioSet = function(config){
36349     
36350     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36351     
36352     this.radioes = [];
36353     
36354     Roo.bootstrap.RadioSet.register(this);
36355     
36356     this.addEvents({
36357         /**
36358         * @event check
36359         * Fires when the element is checked or unchecked.
36360         * @param {Roo.bootstrap.RadioSet} this This radio
36361         * @param {Roo.bootstrap.Radio} item The checked item
36362         */
36363        check : true,
36364        /**
36365         * @event click
36366         * Fires when the element is click.
36367         * @param {Roo.bootstrap.RadioSet} this This radio set
36368         * @param {Roo.bootstrap.Radio} item The checked item
36369         * @param {Roo.EventObject} e The event object
36370         */
36371        click : true
36372     });
36373     
36374 };
36375
36376 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36377
36378     radioes : false,
36379     
36380     inline : true,
36381     
36382     weight : '',
36383     
36384     indicatorpos : 'left',
36385     
36386     getAutoCreate : function()
36387     {
36388         var label = {
36389             tag : 'label',
36390             cls : 'roo-radio-set-label',
36391             cn : [
36392                 {
36393                     tag : 'span',
36394                     html : this.fieldLabel
36395                 }
36396             ]
36397         };
36398         if (Roo.bootstrap.version == 3) {
36399             
36400             
36401             if(this.indicatorpos == 'left'){
36402                 label.cn.unshift({
36403                     tag : 'i',
36404                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36405                     tooltip : 'This field is required'
36406                 });
36407             } else {
36408                 label.cn.push({
36409                     tag : 'i',
36410                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36411                     tooltip : 'This field is required'
36412                 });
36413             }
36414         }
36415         var items = {
36416             tag : 'div',
36417             cls : 'roo-radio-set-items'
36418         };
36419         
36420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36421         
36422         if (align === 'left' && this.fieldLabel.length) {
36423             
36424             items = {
36425                 cls : "roo-radio-set-right", 
36426                 cn: [
36427                     items
36428                 ]
36429             };
36430             
36431             if(this.labelWidth > 12){
36432                 label.style = "width: " + this.labelWidth + 'px';
36433             }
36434             
36435             if(this.labelWidth < 13 && this.labelmd == 0){
36436                 this.labelmd = this.labelWidth;
36437             }
36438             
36439             if(this.labellg > 0){
36440                 label.cls += ' col-lg-' + this.labellg;
36441                 items.cls += ' col-lg-' + (12 - this.labellg);
36442             }
36443             
36444             if(this.labelmd > 0){
36445                 label.cls += ' col-md-' + this.labelmd;
36446                 items.cls += ' col-md-' + (12 - this.labelmd);
36447             }
36448             
36449             if(this.labelsm > 0){
36450                 label.cls += ' col-sm-' + this.labelsm;
36451                 items.cls += ' col-sm-' + (12 - this.labelsm);
36452             }
36453             
36454             if(this.labelxs > 0){
36455                 label.cls += ' col-xs-' + this.labelxs;
36456                 items.cls += ' col-xs-' + (12 - this.labelxs);
36457             }
36458         }
36459         
36460         var cfg = {
36461             tag : 'div',
36462             cls : 'roo-radio-set',
36463             cn : [
36464                 {
36465                     tag : 'input',
36466                     cls : 'roo-radio-set-input',
36467                     type : 'hidden',
36468                     name : this.name,
36469                     value : this.value ? this.value :  ''
36470                 },
36471                 label,
36472                 items
36473             ]
36474         };
36475         
36476         if(this.weight.length){
36477             cfg.cls += ' roo-radio-' + this.weight;
36478         }
36479         
36480         if(this.inline) {
36481             cfg.cls += ' roo-radio-set-inline';
36482         }
36483         
36484         var settings=this;
36485         ['xs','sm','md','lg'].map(function(size){
36486             if (settings[size]) {
36487                 cfg.cls += ' col-' + size + '-' + settings[size];
36488             }
36489         });
36490         
36491         return cfg;
36492         
36493     },
36494
36495     initEvents : function()
36496     {
36497         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36498         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36499         
36500         if(!this.fieldLabel.length){
36501             this.labelEl.hide();
36502         }
36503         
36504         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36505         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36506         
36507         this.indicator = this.indicatorEl();
36508         
36509         if(this.indicator){
36510             this.indicator.addClass('invisible');
36511         }
36512         
36513         this.originalValue = this.getValue();
36514         
36515     },
36516     
36517     inputEl: function ()
36518     {
36519         return this.el.select('.roo-radio-set-input', true).first();
36520     },
36521     
36522     getChildContainer : function()
36523     {
36524         return this.itemsEl;
36525     },
36526     
36527     register : function(item)
36528     {
36529         this.radioes.push(item);
36530         
36531     },
36532     
36533     validate : function()
36534     {   
36535         if(this.getVisibilityEl().hasClass('hidden')){
36536             return true;
36537         }
36538         
36539         var valid = false;
36540         
36541         Roo.each(this.radioes, function(i){
36542             if(!i.checked){
36543                 return;
36544             }
36545             
36546             valid = true;
36547             return false;
36548         });
36549         
36550         if(this.allowBlank) {
36551             return true;
36552         }
36553         
36554         if(this.disabled || valid){
36555             this.markValid();
36556             return true;
36557         }
36558         
36559         this.markInvalid();
36560         return false;
36561         
36562     },
36563     
36564     markValid : function()
36565     {
36566         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36567             this.indicatorEl().removeClass('visible');
36568             this.indicatorEl().addClass('invisible');
36569         }
36570         
36571         
36572         if (Roo.bootstrap.version == 3) {
36573             this.el.removeClass([this.invalidClass, this.validClass]);
36574             this.el.addClass(this.validClass);
36575         } else {
36576             this.el.removeClass(['is-invalid','is-valid']);
36577             this.el.addClass(['is-valid']);
36578         }
36579         this.fireEvent('valid', this);
36580     },
36581     
36582     markInvalid : function(msg)
36583     {
36584         if(this.allowBlank || this.disabled){
36585             return;
36586         }
36587         
36588         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36589             this.indicatorEl().removeClass('invisible');
36590             this.indicatorEl().addClass('visible');
36591         }
36592         if (Roo.bootstrap.version == 3) {
36593             this.el.removeClass([this.invalidClass, this.validClass]);
36594             this.el.addClass(this.invalidClass);
36595         } else {
36596             this.el.removeClass(['is-invalid','is-valid']);
36597             this.el.addClass(['is-invalid']);
36598         }
36599         
36600         this.fireEvent('invalid', this, msg);
36601         
36602     },
36603     
36604     setValue : function(v, suppressEvent)
36605     {   
36606         if(this.value === v){
36607             return;
36608         }
36609         
36610         this.value = v;
36611         
36612         if(this.rendered){
36613             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36614         }
36615         
36616         Roo.each(this.radioes, function(i){
36617             i.checked = false;
36618             i.el.removeClass('checked');
36619         });
36620         
36621         Roo.each(this.radioes, function(i){
36622             
36623             if(i.value === v || i.value.toString() === v.toString()){
36624                 i.checked = true;
36625                 i.el.addClass('checked');
36626                 
36627                 if(suppressEvent !== true){
36628                     this.fireEvent('check', this, i);
36629                 }
36630                 
36631                 return false;
36632             }
36633             
36634         }, this);
36635         
36636         this.validate();
36637     },
36638     
36639     clearInvalid : function(){
36640         
36641         if(!this.el || this.preventMark){
36642             return;
36643         }
36644         
36645         this.el.removeClass([this.invalidClass]);
36646         
36647         this.fireEvent('valid', this);
36648     }
36649     
36650 });
36651
36652 Roo.apply(Roo.bootstrap.RadioSet, {
36653     
36654     groups: {},
36655     
36656     register : function(set)
36657     {
36658         this.groups[set.name] = set;
36659     },
36660     
36661     get: function(name) 
36662     {
36663         if (typeof(this.groups[name]) == 'undefined') {
36664             return false;
36665         }
36666         
36667         return this.groups[name] ;
36668     }
36669     
36670 });
36671 /*
36672  * Based on:
36673  * Ext JS Library 1.1.1
36674  * Copyright(c) 2006-2007, Ext JS, LLC.
36675  *
36676  * Originally Released Under LGPL - original licence link has changed is not relivant.
36677  *
36678  * Fork - LGPL
36679  * <script type="text/javascript">
36680  */
36681
36682
36683 /**
36684  * @class Roo.bootstrap.SplitBar
36685  * @extends Roo.util.Observable
36686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36687  * <br><br>
36688  * Usage:
36689  * <pre><code>
36690 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36691                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36692 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36693 split.minSize = 100;
36694 split.maxSize = 600;
36695 split.animate = true;
36696 split.on('moved', splitterMoved);
36697 </code></pre>
36698  * @constructor
36699  * Create a new SplitBar
36700  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36701  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36702  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36703  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36704                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36705                         position of the SplitBar).
36706  */
36707 Roo.bootstrap.SplitBar = function(cfg){
36708     
36709     /** @private */
36710     
36711     //{
36712     //  dragElement : elm
36713     //  resizingElement: el,
36714         // optional..
36715     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36716     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36717         // existingProxy ???
36718     //}
36719     
36720     this.el = Roo.get(cfg.dragElement, true);
36721     this.el.dom.unselectable = "on";
36722     /** @private */
36723     this.resizingEl = Roo.get(cfg.resizingElement, true);
36724
36725     /**
36726      * @private
36727      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36728      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36729      * @type Number
36730      */
36731     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36732     
36733     /**
36734      * The minimum size of the resizing element. (Defaults to 0)
36735      * @type Number
36736      */
36737     this.minSize = 0;
36738     
36739     /**
36740      * The maximum size of the resizing element. (Defaults to 2000)
36741      * @type Number
36742      */
36743     this.maxSize = 2000;
36744     
36745     /**
36746      * Whether to animate the transition to the new size
36747      * @type Boolean
36748      */
36749     this.animate = false;
36750     
36751     /**
36752      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36753      * @type Boolean
36754      */
36755     this.useShim = false;
36756     
36757     /** @private */
36758     this.shim = null;
36759     
36760     if(!cfg.existingProxy){
36761         /** @private */
36762         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36763     }else{
36764         this.proxy = Roo.get(cfg.existingProxy).dom;
36765     }
36766     /** @private */
36767     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36768     
36769     /** @private */
36770     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36771     
36772     /** @private */
36773     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36774     
36775     /** @private */
36776     this.dragSpecs = {};
36777     
36778     /**
36779      * @private The adapter to use to positon and resize elements
36780      */
36781     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36782     this.adapter.init(this);
36783     
36784     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36785         /** @private */
36786         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36787         this.el.addClass("roo-splitbar-h");
36788     }else{
36789         /** @private */
36790         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36791         this.el.addClass("roo-splitbar-v");
36792     }
36793     
36794     this.addEvents({
36795         /**
36796          * @event resize
36797          * Fires when the splitter is moved (alias for {@link #event-moved})
36798          * @param {Roo.bootstrap.SplitBar} this
36799          * @param {Number} newSize the new width or height
36800          */
36801         "resize" : true,
36802         /**
36803          * @event moved
36804          * Fires when the splitter is moved
36805          * @param {Roo.bootstrap.SplitBar} this
36806          * @param {Number} newSize the new width or height
36807          */
36808         "moved" : true,
36809         /**
36810          * @event beforeresize
36811          * Fires before the splitter is dragged
36812          * @param {Roo.bootstrap.SplitBar} this
36813          */
36814         "beforeresize" : true,
36815
36816         "beforeapply" : true
36817     });
36818
36819     Roo.util.Observable.call(this);
36820 };
36821
36822 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36823     onStartProxyDrag : function(x, y){
36824         this.fireEvent("beforeresize", this);
36825         if(!this.overlay){
36826             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36827             o.unselectable();
36828             o.enableDisplayMode("block");
36829             // all splitbars share the same overlay
36830             Roo.bootstrap.SplitBar.prototype.overlay = o;
36831         }
36832         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36833         this.overlay.show();
36834         Roo.get(this.proxy).setDisplayed("block");
36835         var size = this.adapter.getElementSize(this);
36836         this.activeMinSize = this.getMinimumSize();;
36837         this.activeMaxSize = this.getMaximumSize();;
36838         var c1 = size - this.activeMinSize;
36839         var c2 = Math.max(this.activeMaxSize - size, 0);
36840         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36841             this.dd.resetConstraints();
36842             this.dd.setXConstraint(
36843                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36844                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36845             );
36846             this.dd.setYConstraint(0, 0);
36847         }else{
36848             this.dd.resetConstraints();
36849             this.dd.setXConstraint(0, 0);
36850             this.dd.setYConstraint(
36851                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36852                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36853             );
36854          }
36855         this.dragSpecs.startSize = size;
36856         this.dragSpecs.startPoint = [x, y];
36857         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36858     },
36859     
36860     /** 
36861      * @private Called after the drag operation by the DDProxy
36862      */
36863     onEndProxyDrag : function(e){
36864         Roo.get(this.proxy).setDisplayed(false);
36865         var endPoint = Roo.lib.Event.getXY(e);
36866         if(this.overlay){
36867             this.overlay.hide();
36868         }
36869         var newSize;
36870         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36871             newSize = this.dragSpecs.startSize + 
36872                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36873                     endPoint[0] - this.dragSpecs.startPoint[0] :
36874                     this.dragSpecs.startPoint[0] - endPoint[0]
36875                 );
36876         }else{
36877             newSize = this.dragSpecs.startSize + 
36878                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36879                     endPoint[1] - this.dragSpecs.startPoint[1] :
36880                     this.dragSpecs.startPoint[1] - endPoint[1]
36881                 );
36882         }
36883         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36884         if(newSize != this.dragSpecs.startSize){
36885             if(this.fireEvent('beforeapply', this, newSize) !== false){
36886                 this.adapter.setElementSize(this, newSize);
36887                 this.fireEvent("moved", this, newSize);
36888                 this.fireEvent("resize", this, newSize);
36889             }
36890         }
36891     },
36892     
36893     /**
36894      * Get the adapter this SplitBar uses
36895      * @return The adapter object
36896      */
36897     getAdapter : function(){
36898         return this.adapter;
36899     },
36900     
36901     /**
36902      * Set the adapter this SplitBar uses
36903      * @param {Object} adapter A SplitBar adapter object
36904      */
36905     setAdapter : function(adapter){
36906         this.adapter = adapter;
36907         this.adapter.init(this);
36908     },
36909     
36910     /**
36911      * Gets the minimum size for the resizing element
36912      * @return {Number} The minimum size
36913      */
36914     getMinimumSize : function(){
36915         return this.minSize;
36916     },
36917     
36918     /**
36919      * Sets the minimum size for the resizing element
36920      * @param {Number} minSize The minimum size
36921      */
36922     setMinimumSize : function(minSize){
36923         this.minSize = minSize;
36924     },
36925     
36926     /**
36927      * Gets the maximum size for the resizing element
36928      * @return {Number} The maximum size
36929      */
36930     getMaximumSize : function(){
36931         return this.maxSize;
36932     },
36933     
36934     /**
36935      * Sets the maximum size for the resizing element
36936      * @param {Number} maxSize The maximum size
36937      */
36938     setMaximumSize : function(maxSize){
36939         this.maxSize = maxSize;
36940     },
36941     
36942     /**
36943      * Sets the initialize size for the resizing element
36944      * @param {Number} size The initial size
36945      */
36946     setCurrentSize : function(size){
36947         var oldAnimate = this.animate;
36948         this.animate = false;
36949         this.adapter.setElementSize(this, size);
36950         this.animate = oldAnimate;
36951     },
36952     
36953     /**
36954      * Destroy this splitbar. 
36955      * @param {Boolean} removeEl True to remove the element
36956      */
36957     destroy : function(removeEl){
36958         if(this.shim){
36959             this.shim.remove();
36960         }
36961         this.dd.unreg();
36962         this.proxy.parentNode.removeChild(this.proxy);
36963         if(removeEl){
36964             this.el.remove();
36965         }
36966     }
36967 });
36968
36969 /**
36970  * @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.
36971  */
36972 Roo.bootstrap.SplitBar.createProxy = function(dir){
36973     var proxy = new Roo.Element(document.createElement("div"));
36974     proxy.unselectable();
36975     var cls = 'roo-splitbar-proxy';
36976     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36977     document.body.appendChild(proxy.dom);
36978     return proxy.dom;
36979 };
36980
36981 /** 
36982  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36983  * Default Adapter. It assumes the splitter and resizing element are not positioned
36984  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36985  */
36986 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36987 };
36988
36989 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36990     // do nothing for now
36991     init : function(s){
36992     
36993     },
36994     /**
36995      * Called before drag operations to get the current size of the resizing element. 
36996      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36997      */
36998      getElementSize : function(s){
36999         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37000             return s.resizingEl.getWidth();
37001         }else{
37002             return s.resizingEl.getHeight();
37003         }
37004     },
37005     
37006     /**
37007      * Called after drag operations to set the size of the resizing element.
37008      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37009      * @param {Number} newSize The new size to set
37010      * @param {Function} onComplete A function to be invoked when resizing is complete
37011      */
37012     setElementSize : function(s, newSize, onComplete){
37013         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37014             if(!s.animate){
37015                 s.resizingEl.setWidth(newSize);
37016                 if(onComplete){
37017                     onComplete(s, newSize);
37018                 }
37019             }else{
37020                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37021             }
37022         }else{
37023             
37024             if(!s.animate){
37025                 s.resizingEl.setHeight(newSize);
37026                 if(onComplete){
37027                     onComplete(s, newSize);
37028                 }
37029             }else{
37030                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37031             }
37032         }
37033     }
37034 };
37035
37036 /** 
37037  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37038  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37039  * Adapter that  moves the splitter element to align with the resized sizing element. 
37040  * Used with an absolute positioned SplitBar.
37041  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37042  * document.body, make sure you assign an id to the body element.
37043  */
37044 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37045     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37046     this.container = Roo.get(container);
37047 };
37048
37049 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37050     init : function(s){
37051         this.basic.init(s);
37052     },
37053     
37054     getElementSize : function(s){
37055         return this.basic.getElementSize(s);
37056     },
37057     
37058     setElementSize : function(s, newSize, onComplete){
37059         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37060     },
37061     
37062     moveSplitter : function(s){
37063         var yes = Roo.bootstrap.SplitBar;
37064         switch(s.placement){
37065             case yes.LEFT:
37066                 s.el.setX(s.resizingEl.getRight());
37067                 break;
37068             case yes.RIGHT:
37069                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37070                 break;
37071             case yes.TOP:
37072                 s.el.setY(s.resizingEl.getBottom());
37073                 break;
37074             case yes.BOTTOM:
37075                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37076                 break;
37077         }
37078     }
37079 };
37080
37081 /**
37082  * Orientation constant - Create a vertical SplitBar
37083  * @static
37084  * @type Number
37085  */
37086 Roo.bootstrap.SplitBar.VERTICAL = 1;
37087
37088 /**
37089  * Orientation constant - Create a horizontal SplitBar
37090  * @static
37091  * @type Number
37092  */
37093 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37094
37095 /**
37096  * Placement constant - The resizing element is to the left of the splitter element
37097  * @static
37098  * @type Number
37099  */
37100 Roo.bootstrap.SplitBar.LEFT = 1;
37101
37102 /**
37103  * Placement constant - The resizing element is to the right of the splitter element
37104  * @static
37105  * @type Number
37106  */
37107 Roo.bootstrap.SplitBar.RIGHT = 2;
37108
37109 /**
37110  * Placement constant - The resizing element is positioned above the splitter element
37111  * @static
37112  * @type Number
37113  */
37114 Roo.bootstrap.SplitBar.TOP = 3;
37115
37116 /**
37117  * Placement constant - The resizing element is positioned under splitter element
37118  * @static
37119  * @type Number
37120  */
37121 Roo.bootstrap.SplitBar.BOTTOM = 4;
37122 Roo.namespace("Roo.bootstrap.layout");/*
37123  * Based on:
37124  * Ext JS Library 1.1.1
37125  * Copyright(c) 2006-2007, Ext JS, LLC.
37126  *
37127  * Originally Released Under LGPL - original licence link has changed is not relivant.
37128  *
37129  * Fork - LGPL
37130  * <script type="text/javascript">
37131  */
37132
37133 /**
37134  * @class Roo.bootstrap.layout.Manager
37135  * @extends Roo.bootstrap.Component
37136  * Base class for layout managers.
37137  */
37138 Roo.bootstrap.layout.Manager = function(config)
37139 {
37140     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37141
37142
37143
37144
37145
37146     /** false to disable window resize monitoring @type Boolean */
37147     this.monitorWindowResize = true;
37148     this.regions = {};
37149     this.addEvents({
37150         /**
37151          * @event layout
37152          * Fires when a layout is performed.
37153          * @param {Roo.LayoutManager} this
37154          */
37155         "layout" : true,
37156         /**
37157          * @event regionresized
37158          * Fires when the user resizes a region.
37159          * @param {Roo.LayoutRegion} region The resized region
37160          * @param {Number} newSize The new size (width for east/west, height for north/south)
37161          */
37162         "regionresized" : true,
37163         /**
37164          * @event regioncollapsed
37165          * Fires when a region is collapsed.
37166          * @param {Roo.LayoutRegion} region The collapsed region
37167          */
37168         "regioncollapsed" : true,
37169         /**
37170          * @event regionexpanded
37171          * Fires when a region is expanded.
37172          * @param {Roo.LayoutRegion} region The expanded region
37173          */
37174         "regionexpanded" : true
37175     });
37176     this.updating = false;
37177
37178     if (config.el) {
37179         this.el = Roo.get(config.el);
37180         this.initEvents();
37181     }
37182
37183 };
37184
37185 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37186
37187
37188     regions : null,
37189
37190     monitorWindowResize : true,
37191
37192
37193     updating : false,
37194
37195
37196     onRender : function(ct, position)
37197     {
37198         if(!this.el){
37199             this.el = Roo.get(ct);
37200             this.initEvents();
37201         }
37202         //this.fireEvent('render',this);
37203     },
37204
37205
37206     initEvents: function()
37207     {
37208
37209
37210         // ie scrollbar fix
37211         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37212             document.body.scroll = "no";
37213         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37214             this.el.position('relative');
37215         }
37216         this.id = this.el.id;
37217         this.el.addClass("roo-layout-container");
37218         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37219         if(this.el.dom != document.body ) {
37220             this.el.on('resize', this.layout,this);
37221             this.el.on('show', this.layout,this);
37222         }
37223
37224     },
37225
37226     /**
37227      * Returns true if this layout is currently being updated
37228      * @return {Boolean}
37229      */
37230     isUpdating : function(){
37231         return this.updating;
37232     },
37233
37234     /**
37235      * Suspend the LayoutManager from doing auto-layouts while
37236      * making multiple add or remove calls
37237      */
37238     beginUpdate : function(){
37239         this.updating = true;
37240     },
37241
37242     /**
37243      * Restore auto-layouts and optionally disable the manager from performing a layout
37244      * @param {Boolean} noLayout true to disable a layout update
37245      */
37246     endUpdate : function(noLayout){
37247         this.updating = false;
37248         if(!noLayout){
37249             this.layout();
37250         }
37251     },
37252
37253     layout: function(){
37254         // abstract...
37255     },
37256
37257     onRegionResized : function(region, newSize){
37258         this.fireEvent("regionresized", region, newSize);
37259         this.layout();
37260     },
37261
37262     onRegionCollapsed : function(region){
37263         this.fireEvent("regioncollapsed", region);
37264     },
37265
37266     onRegionExpanded : function(region){
37267         this.fireEvent("regionexpanded", region);
37268     },
37269
37270     /**
37271      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37272      * performs box-model adjustments.
37273      * @return {Object} The size as an object {width: (the width), height: (the height)}
37274      */
37275     getViewSize : function()
37276     {
37277         var size;
37278         if(this.el.dom != document.body){
37279             size = this.el.getSize();
37280         }else{
37281             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37282         }
37283         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37284         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37285         return size;
37286     },
37287
37288     /**
37289      * Returns the Element this layout is bound to.
37290      * @return {Roo.Element}
37291      */
37292     getEl : function(){
37293         return this.el;
37294     },
37295
37296     /**
37297      * Returns the specified region.
37298      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37299      * @return {Roo.LayoutRegion}
37300      */
37301     getRegion : function(target){
37302         return this.regions[target.toLowerCase()];
37303     },
37304
37305     onWindowResize : function(){
37306         if(this.monitorWindowResize){
37307             this.layout();
37308         }
37309     }
37310 });
37311 /*
37312  * Based on:
37313  * Ext JS Library 1.1.1
37314  * Copyright(c) 2006-2007, Ext JS, LLC.
37315  *
37316  * Originally Released Under LGPL - original licence link has changed is not relivant.
37317  *
37318  * Fork - LGPL
37319  * <script type="text/javascript">
37320  */
37321 /**
37322  * @class Roo.bootstrap.layout.Border
37323  * @extends Roo.bootstrap.layout.Manager
37324  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37325  * please see: examples/bootstrap/nested.html<br><br>
37326  
37327 <b>The container the layout is rendered into can be either the body element or any other element.
37328 If it is not the body element, the container needs to either be an absolute positioned element,
37329 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37330 the container size if it is not the body element.</b>
37331
37332 * @constructor
37333 * Create a new Border
37334 * @param {Object} config Configuration options
37335  */
37336 Roo.bootstrap.layout.Border = function(config){
37337     config = config || {};
37338     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37339     
37340     
37341     
37342     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37343         if(config[region]){
37344             config[region].region = region;
37345             this.addRegion(config[region]);
37346         }
37347     },this);
37348     
37349 };
37350
37351 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37352
37353 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37354     
37355     parent : false, // this might point to a 'nest' or a ???
37356     
37357     /**
37358      * Creates and adds a new region if it doesn't already exist.
37359      * @param {String} target The target region key (north, south, east, west or center).
37360      * @param {Object} config The regions config object
37361      * @return {BorderLayoutRegion} The new region
37362      */
37363     addRegion : function(config)
37364     {
37365         if(!this.regions[config.region]){
37366             var r = this.factory(config);
37367             this.bindRegion(r);
37368         }
37369         return this.regions[config.region];
37370     },
37371
37372     // private (kinda)
37373     bindRegion : function(r){
37374         this.regions[r.config.region] = r;
37375         
37376         r.on("visibilitychange",    this.layout, this);
37377         r.on("paneladded",          this.layout, this);
37378         r.on("panelremoved",        this.layout, this);
37379         r.on("invalidated",         this.layout, this);
37380         r.on("resized",             this.onRegionResized, this);
37381         r.on("collapsed",           this.onRegionCollapsed, this);
37382         r.on("expanded",            this.onRegionExpanded, this);
37383     },
37384
37385     /**
37386      * Performs a layout update.
37387      */
37388     layout : function()
37389     {
37390         if(this.updating) {
37391             return;
37392         }
37393         
37394         // render all the rebions if they have not been done alreayd?
37395         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37396             if(this.regions[region] && !this.regions[region].bodyEl){
37397                 this.regions[region].onRender(this.el)
37398             }
37399         },this);
37400         
37401         var size = this.getViewSize();
37402         var w = size.width;
37403         var h = size.height;
37404         var centerW = w;
37405         var centerH = h;
37406         var centerY = 0;
37407         var centerX = 0;
37408         //var x = 0, y = 0;
37409
37410         var rs = this.regions;
37411         var north = rs["north"];
37412         var south = rs["south"]; 
37413         var west = rs["west"];
37414         var east = rs["east"];
37415         var center = rs["center"];
37416         //if(this.hideOnLayout){ // not supported anymore
37417             //c.el.setStyle("display", "none");
37418         //}
37419         if(north && north.isVisible()){
37420             var b = north.getBox();
37421             var m = north.getMargins();
37422             b.width = w - (m.left+m.right);
37423             b.x = m.left;
37424             b.y = m.top;
37425             centerY = b.height + b.y + m.bottom;
37426             centerH -= centerY;
37427             north.updateBox(this.safeBox(b));
37428         }
37429         if(south && south.isVisible()){
37430             var b = south.getBox();
37431             var m = south.getMargins();
37432             b.width = w - (m.left+m.right);
37433             b.x = m.left;
37434             var totalHeight = (b.height + m.top + m.bottom);
37435             b.y = h - totalHeight + m.top;
37436             centerH -= totalHeight;
37437             south.updateBox(this.safeBox(b));
37438         }
37439         if(west && west.isVisible()){
37440             var b = west.getBox();
37441             var m = west.getMargins();
37442             b.height = centerH - (m.top+m.bottom);
37443             b.x = m.left;
37444             b.y = centerY + m.top;
37445             var totalWidth = (b.width + m.left + m.right);
37446             centerX += totalWidth;
37447             centerW -= totalWidth;
37448             west.updateBox(this.safeBox(b));
37449         }
37450         if(east && east.isVisible()){
37451             var b = east.getBox();
37452             var m = east.getMargins();
37453             b.height = centerH - (m.top+m.bottom);
37454             var totalWidth = (b.width + m.left + m.right);
37455             b.x = w - totalWidth + m.left;
37456             b.y = centerY + m.top;
37457             centerW -= totalWidth;
37458             east.updateBox(this.safeBox(b));
37459         }
37460         if(center){
37461             var m = center.getMargins();
37462             var centerBox = {
37463                 x: centerX + m.left,
37464                 y: centerY + m.top,
37465                 width: centerW - (m.left+m.right),
37466                 height: centerH - (m.top+m.bottom)
37467             };
37468             //if(this.hideOnLayout){
37469                 //center.el.setStyle("display", "block");
37470             //}
37471             center.updateBox(this.safeBox(centerBox));
37472         }
37473         this.el.repaint();
37474         this.fireEvent("layout", this);
37475     },
37476
37477     // private
37478     safeBox : function(box){
37479         box.width = Math.max(0, box.width);
37480         box.height = Math.max(0, box.height);
37481         return box;
37482     },
37483
37484     /**
37485      * Adds a ContentPanel (or subclass) to this layout.
37486      * @param {String} target The target region key (north, south, east, west or center).
37487      * @param {Roo.ContentPanel} panel The panel to add
37488      * @return {Roo.ContentPanel} The added panel
37489      */
37490     add : function(target, panel){
37491          
37492         target = target.toLowerCase();
37493         return this.regions[target].add(panel);
37494     },
37495
37496     /**
37497      * Remove a ContentPanel (or subclass) to this layout.
37498      * @param {String} target The target region key (north, south, east, west or center).
37499      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37500      * @return {Roo.ContentPanel} The removed panel
37501      */
37502     remove : function(target, panel){
37503         target = target.toLowerCase();
37504         return this.regions[target].remove(panel);
37505     },
37506
37507     /**
37508      * Searches all regions for a panel with the specified id
37509      * @param {String} panelId
37510      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37511      */
37512     findPanel : function(panelId){
37513         var rs = this.regions;
37514         for(var target in rs){
37515             if(typeof rs[target] != "function"){
37516                 var p = rs[target].getPanel(panelId);
37517                 if(p){
37518                     return p;
37519                 }
37520             }
37521         }
37522         return null;
37523     },
37524
37525     /**
37526      * Searches all regions for a panel with the specified id and activates (shows) it.
37527      * @param {String/ContentPanel} panelId The panels id or the panel itself
37528      * @return {Roo.ContentPanel} The shown panel or null
37529      */
37530     showPanel : function(panelId) {
37531       var rs = this.regions;
37532       for(var target in rs){
37533          var r = rs[target];
37534          if(typeof r != "function"){
37535             if(r.hasPanel(panelId)){
37536                return r.showPanel(panelId);
37537             }
37538          }
37539       }
37540       return null;
37541    },
37542
37543    /**
37544      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37545      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37546      */
37547    /*
37548     restoreState : function(provider){
37549         if(!provider){
37550             provider = Roo.state.Manager;
37551         }
37552         var sm = new Roo.LayoutStateManager();
37553         sm.init(this, provider);
37554     },
37555 */
37556  
37557  
37558     /**
37559      * Adds a xtype elements to the layout.
37560      * <pre><code>
37561
37562 layout.addxtype({
37563        xtype : 'ContentPanel',
37564        region: 'west',
37565        items: [ .... ]
37566    }
37567 );
37568
37569 layout.addxtype({
37570         xtype : 'NestedLayoutPanel',
37571         region: 'west',
37572         layout: {
37573            center: { },
37574            west: { }   
37575         },
37576         items : [ ... list of content panels or nested layout panels.. ]
37577    }
37578 );
37579 </code></pre>
37580      * @param {Object} cfg Xtype definition of item to add.
37581      */
37582     addxtype : function(cfg)
37583     {
37584         // basically accepts a pannel...
37585         // can accept a layout region..!?!?
37586         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37587         
37588         
37589         // theory?  children can only be panels??
37590         
37591         //if (!cfg.xtype.match(/Panel$/)) {
37592         //    return false;
37593         //}
37594         var ret = false;
37595         
37596         if (typeof(cfg.region) == 'undefined') {
37597             Roo.log("Failed to add Panel, region was not set");
37598             Roo.log(cfg);
37599             return false;
37600         }
37601         var region = cfg.region;
37602         delete cfg.region;
37603         
37604           
37605         var xitems = [];
37606         if (cfg.items) {
37607             xitems = cfg.items;
37608             delete cfg.items;
37609         }
37610         var nb = false;
37611         
37612         if ( region == 'center') {
37613             Roo.log("Center: " + cfg.title);
37614         }
37615         
37616         
37617         switch(cfg.xtype) 
37618         {
37619             case 'Content':  // ContentPanel (el, cfg)
37620             case 'Scroll':  // ContentPanel (el, cfg)
37621             case 'View': 
37622                 cfg.autoCreate = cfg.autoCreate || true;
37623                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37624                 //} else {
37625                 //    var el = this.el.createChild();
37626                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37627                 //}
37628                 
37629                 this.add(region, ret);
37630                 break;
37631             
37632             /*
37633             case 'TreePanel': // our new panel!
37634                 cfg.el = this.el.createChild();
37635                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37636                 this.add(region, ret);
37637                 break;
37638             */
37639             
37640             case 'Nest': 
37641                 // create a new Layout (which is  a Border Layout...
37642                 
37643                 var clayout = cfg.layout;
37644                 clayout.el  = this.el.createChild();
37645                 clayout.items   = clayout.items  || [];
37646                 
37647                 delete cfg.layout;
37648                 
37649                 // replace this exitems with the clayout ones..
37650                 xitems = clayout.items;
37651                  
37652                 // force background off if it's in center...
37653                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37654                     cfg.background = false;
37655                 }
37656                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37657                 
37658                 
37659                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37660                 //console.log('adding nested layout panel '  + cfg.toSource());
37661                 this.add(region, ret);
37662                 nb = {}; /// find first...
37663                 break;
37664             
37665             case 'Grid':
37666                 
37667                 // needs grid and region
37668                 
37669                 //var el = this.getRegion(region).el.createChild();
37670                 /*
37671                  *var el = this.el.createChild();
37672                 // create the grid first...
37673                 cfg.grid.container = el;
37674                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37675                 */
37676                 
37677                 if (region == 'center' && this.active ) {
37678                     cfg.background = false;
37679                 }
37680                 
37681                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37682                 
37683                 this.add(region, ret);
37684                 /*
37685                 if (cfg.background) {
37686                     // render grid on panel activation (if panel background)
37687                     ret.on('activate', function(gp) {
37688                         if (!gp.grid.rendered) {
37689                     //        gp.grid.render(el);
37690                         }
37691                     });
37692                 } else {
37693                   //  cfg.grid.render(el);
37694                 }
37695                 */
37696                 break;
37697            
37698            
37699             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37700                 // it was the old xcomponent building that caused this before.
37701                 // espeically if border is the top element in the tree.
37702                 ret = this;
37703                 break; 
37704                 
37705                     
37706                 
37707                 
37708                 
37709             default:
37710                 /*
37711                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37712                     
37713                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37714                     this.add(region, ret);
37715                 } else {
37716                 */
37717                     Roo.log(cfg);
37718                     throw "Can not add '" + cfg.xtype + "' to Border";
37719                     return null;
37720              
37721                                 
37722              
37723         }
37724         this.beginUpdate();
37725         // add children..
37726         var region = '';
37727         var abn = {};
37728         Roo.each(xitems, function(i)  {
37729             region = nb && i.region ? i.region : false;
37730             
37731             var add = ret.addxtype(i);
37732            
37733             if (region) {
37734                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37735                 if (!i.background) {
37736                     abn[region] = nb[region] ;
37737                 }
37738             }
37739             
37740         });
37741         this.endUpdate();
37742
37743         // make the last non-background panel active..
37744         //if (nb) { Roo.log(abn); }
37745         if (nb) {
37746             
37747             for(var r in abn) {
37748                 region = this.getRegion(r);
37749                 if (region) {
37750                     // tried using nb[r], but it does not work..
37751                      
37752                     region.showPanel(abn[r]);
37753                    
37754                 }
37755             }
37756         }
37757         return ret;
37758         
37759     },
37760     
37761     
37762 // private
37763     factory : function(cfg)
37764     {
37765         
37766         var validRegions = Roo.bootstrap.layout.Border.regions;
37767
37768         var target = cfg.region;
37769         cfg.mgr = this;
37770         
37771         var r = Roo.bootstrap.layout;
37772         Roo.log(target);
37773         switch(target){
37774             case "north":
37775                 return new r.North(cfg);
37776             case "south":
37777                 return new r.South(cfg);
37778             case "east":
37779                 return new r.East(cfg);
37780             case "west":
37781                 return new r.West(cfg);
37782             case "center":
37783                 return new r.Center(cfg);
37784         }
37785         throw 'Layout region "'+target+'" not supported.';
37786     }
37787     
37788     
37789 });
37790  /*
37791  * Based on:
37792  * Ext JS Library 1.1.1
37793  * Copyright(c) 2006-2007, Ext JS, LLC.
37794  *
37795  * Originally Released Under LGPL - original licence link has changed is not relivant.
37796  *
37797  * Fork - LGPL
37798  * <script type="text/javascript">
37799  */
37800  
37801 /**
37802  * @class Roo.bootstrap.layout.Basic
37803  * @extends Roo.util.Observable
37804  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37805  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37806  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37807  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37808  * @cfg {string}   region  the region that it inhabits..
37809  * @cfg {bool}   skipConfig skip config?
37810  * 
37811
37812  */
37813 Roo.bootstrap.layout.Basic = function(config){
37814     
37815     this.mgr = config.mgr;
37816     
37817     this.position = config.region;
37818     
37819     var skipConfig = config.skipConfig;
37820     
37821     this.events = {
37822         /**
37823          * @scope Roo.BasicLayoutRegion
37824          */
37825         
37826         /**
37827          * @event beforeremove
37828          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37829          * @param {Roo.LayoutRegion} this
37830          * @param {Roo.ContentPanel} panel The panel
37831          * @param {Object} e The cancel event object
37832          */
37833         "beforeremove" : true,
37834         /**
37835          * @event invalidated
37836          * Fires when the layout for this region is changed.
37837          * @param {Roo.LayoutRegion} this
37838          */
37839         "invalidated" : true,
37840         /**
37841          * @event visibilitychange
37842          * Fires when this region is shown or hidden 
37843          * @param {Roo.LayoutRegion} this
37844          * @param {Boolean} visibility true or false
37845          */
37846         "visibilitychange" : true,
37847         /**
37848          * @event paneladded
37849          * Fires when a panel is added. 
37850          * @param {Roo.LayoutRegion} this
37851          * @param {Roo.ContentPanel} panel The panel
37852          */
37853         "paneladded" : true,
37854         /**
37855          * @event panelremoved
37856          * Fires when a panel is removed. 
37857          * @param {Roo.LayoutRegion} this
37858          * @param {Roo.ContentPanel} panel The panel
37859          */
37860         "panelremoved" : true,
37861         /**
37862          * @event beforecollapse
37863          * Fires when this region before collapse.
37864          * @param {Roo.LayoutRegion} this
37865          */
37866         "beforecollapse" : true,
37867         /**
37868          * @event collapsed
37869          * Fires when this region is collapsed.
37870          * @param {Roo.LayoutRegion} this
37871          */
37872         "collapsed" : true,
37873         /**
37874          * @event expanded
37875          * Fires when this region is expanded.
37876          * @param {Roo.LayoutRegion} this
37877          */
37878         "expanded" : true,
37879         /**
37880          * @event slideshow
37881          * Fires when this region is slid into view.
37882          * @param {Roo.LayoutRegion} this
37883          */
37884         "slideshow" : true,
37885         /**
37886          * @event slidehide
37887          * Fires when this region slides out of view. 
37888          * @param {Roo.LayoutRegion} this
37889          */
37890         "slidehide" : true,
37891         /**
37892          * @event panelactivated
37893          * Fires when a panel is activated. 
37894          * @param {Roo.LayoutRegion} this
37895          * @param {Roo.ContentPanel} panel The activated panel
37896          */
37897         "panelactivated" : true,
37898         /**
37899          * @event resized
37900          * Fires when the user resizes this region. 
37901          * @param {Roo.LayoutRegion} this
37902          * @param {Number} newSize The new size (width for east/west, height for north/south)
37903          */
37904         "resized" : true
37905     };
37906     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37907     this.panels = new Roo.util.MixedCollection();
37908     this.panels.getKey = this.getPanelId.createDelegate(this);
37909     this.box = null;
37910     this.activePanel = null;
37911     // ensure listeners are added...
37912     
37913     if (config.listeners || config.events) {
37914         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37915             listeners : config.listeners || {},
37916             events : config.events || {}
37917         });
37918     }
37919     
37920     if(skipConfig !== true){
37921         this.applyConfig(config);
37922     }
37923 };
37924
37925 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37926 {
37927     getPanelId : function(p){
37928         return p.getId();
37929     },
37930     
37931     applyConfig : function(config){
37932         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37933         this.config = config;
37934         
37935     },
37936     
37937     /**
37938      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37939      * the width, for horizontal (north, south) the height.
37940      * @param {Number} newSize The new width or height
37941      */
37942     resizeTo : function(newSize){
37943         var el = this.el ? this.el :
37944                  (this.activePanel ? this.activePanel.getEl() : null);
37945         if(el){
37946             switch(this.position){
37947                 case "east":
37948                 case "west":
37949                     el.setWidth(newSize);
37950                     this.fireEvent("resized", this, newSize);
37951                 break;
37952                 case "north":
37953                 case "south":
37954                     el.setHeight(newSize);
37955                     this.fireEvent("resized", this, newSize);
37956                 break;                
37957             }
37958         }
37959     },
37960     
37961     getBox : function(){
37962         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37963     },
37964     
37965     getMargins : function(){
37966         return this.margins;
37967     },
37968     
37969     updateBox : function(box){
37970         this.box = box;
37971         var el = this.activePanel.getEl();
37972         el.dom.style.left = box.x + "px";
37973         el.dom.style.top = box.y + "px";
37974         this.activePanel.setSize(box.width, box.height);
37975     },
37976     
37977     /**
37978      * Returns the container element for this region.
37979      * @return {Roo.Element}
37980      */
37981     getEl : function(){
37982         return this.activePanel;
37983     },
37984     
37985     /**
37986      * Returns true if this region is currently visible.
37987      * @return {Boolean}
37988      */
37989     isVisible : function(){
37990         return this.activePanel ? true : false;
37991     },
37992     
37993     setActivePanel : function(panel){
37994         panel = this.getPanel(panel);
37995         if(this.activePanel && this.activePanel != panel){
37996             this.activePanel.setActiveState(false);
37997             this.activePanel.getEl().setLeftTop(-10000,-10000);
37998         }
37999         this.activePanel = panel;
38000         panel.setActiveState(true);
38001         if(this.box){
38002             panel.setSize(this.box.width, this.box.height);
38003         }
38004         this.fireEvent("panelactivated", this, panel);
38005         this.fireEvent("invalidated");
38006     },
38007     
38008     /**
38009      * Show the specified panel.
38010      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38011      * @return {Roo.ContentPanel} The shown panel or null
38012      */
38013     showPanel : function(panel){
38014         panel = this.getPanel(panel);
38015         if(panel){
38016             this.setActivePanel(panel);
38017         }
38018         return panel;
38019     },
38020     
38021     /**
38022      * Get the active panel for this region.
38023      * @return {Roo.ContentPanel} The active panel or null
38024      */
38025     getActivePanel : function(){
38026         return this.activePanel;
38027     },
38028     
38029     /**
38030      * Add the passed ContentPanel(s)
38031      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38032      * @return {Roo.ContentPanel} The panel added (if only one was added)
38033      */
38034     add : function(panel){
38035         if(arguments.length > 1){
38036             for(var i = 0, len = arguments.length; i < len; i++) {
38037                 this.add(arguments[i]);
38038             }
38039             return null;
38040         }
38041         if(this.hasPanel(panel)){
38042             this.showPanel(panel);
38043             return panel;
38044         }
38045         var el = panel.getEl();
38046         if(el.dom.parentNode != this.mgr.el.dom){
38047             this.mgr.el.dom.appendChild(el.dom);
38048         }
38049         if(panel.setRegion){
38050             panel.setRegion(this);
38051         }
38052         this.panels.add(panel);
38053         el.setStyle("position", "absolute");
38054         if(!panel.background){
38055             this.setActivePanel(panel);
38056             if(this.config.initialSize && this.panels.getCount()==1){
38057                 this.resizeTo(this.config.initialSize);
38058             }
38059         }
38060         this.fireEvent("paneladded", this, panel);
38061         return panel;
38062     },
38063     
38064     /**
38065      * Returns true if the panel is in this region.
38066      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38067      * @return {Boolean}
38068      */
38069     hasPanel : function(panel){
38070         if(typeof panel == "object"){ // must be panel obj
38071             panel = panel.getId();
38072         }
38073         return this.getPanel(panel) ? true : false;
38074     },
38075     
38076     /**
38077      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38078      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38079      * @param {Boolean} preservePanel Overrides the config preservePanel option
38080      * @return {Roo.ContentPanel} The panel that was removed
38081      */
38082     remove : function(panel, preservePanel){
38083         panel = this.getPanel(panel);
38084         if(!panel){
38085             return null;
38086         }
38087         var e = {};
38088         this.fireEvent("beforeremove", this, panel, e);
38089         if(e.cancel === true){
38090             return null;
38091         }
38092         var panelId = panel.getId();
38093         this.panels.removeKey(panelId);
38094         return panel;
38095     },
38096     
38097     /**
38098      * Returns the panel specified or null if it's not in this region.
38099      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38100      * @return {Roo.ContentPanel}
38101      */
38102     getPanel : function(id){
38103         if(typeof id == "object"){ // must be panel obj
38104             return id;
38105         }
38106         return this.panels.get(id);
38107     },
38108     
38109     /**
38110      * Returns this regions position (north/south/east/west/center).
38111      * @return {String} 
38112      */
38113     getPosition: function(){
38114         return this.position;    
38115     }
38116 });/*
38117  * Based on:
38118  * Ext JS Library 1.1.1
38119  * Copyright(c) 2006-2007, Ext JS, LLC.
38120  *
38121  * Originally Released Under LGPL - original licence link has changed is not relivant.
38122  *
38123  * Fork - LGPL
38124  * <script type="text/javascript">
38125  */
38126  
38127 /**
38128  * @class Roo.bootstrap.layout.Region
38129  * @extends Roo.bootstrap.layout.Basic
38130  * This class represents a region in a layout manager.
38131  
38132  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38133  * @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})
38134  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38135  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38136  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38137  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38138  * @cfg {String}    title           The title for the region (overrides panel titles)
38139  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38140  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38141  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38142  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38143  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38144  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38145  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38146  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38147  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38148  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38149
38150  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38151  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38152  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38153  * @cfg {Number}    width           For East/West panels
38154  * @cfg {Number}    height          For North/South panels
38155  * @cfg {Boolean}   split           To show the splitter
38156  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38157  * 
38158  * @cfg {string}   cls             Extra CSS classes to add to region
38159  * 
38160  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38161  * @cfg {string}   region  the region that it inhabits..
38162  *
38163
38164  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38165  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38166
38167  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38168  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38169  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38170  */
38171 Roo.bootstrap.layout.Region = function(config)
38172 {
38173     this.applyConfig(config);
38174
38175     var mgr = config.mgr;
38176     var pos = config.region;
38177     config.skipConfig = true;
38178     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38179     
38180     if (mgr.el) {
38181         this.onRender(mgr.el);   
38182     }
38183      
38184     this.visible = true;
38185     this.collapsed = false;
38186     this.unrendered_panels = [];
38187 };
38188
38189 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38190
38191     position: '', // set by wrapper (eg. north/south etc..)
38192     unrendered_panels : null,  // unrendered panels.
38193     
38194     tabPosition : false,
38195     
38196     mgr: false, // points to 'Border'
38197     
38198     
38199     createBody : function(){
38200         /** This region's body element 
38201         * @type Roo.Element */
38202         this.bodyEl = this.el.createChild({
38203                 tag: "div",
38204                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38205         });
38206     },
38207
38208     onRender: function(ctr, pos)
38209     {
38210         var dh = Roo.DomHelper;
38211         /** This region's container element 
38212         * @type Roo.Element */
38213         this.el = dh.append(ctr.dom, {
38214                 tag: "div",
38215                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38216             }, true);
38217         /** This region's title element 
38218         * @type Roo.Element */
38219     
38220         this.titleEl = dh.append(this.el.dom,  {
38221                 tag: "div",
38222                 unselectable: "on",
38223                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38224                 children:[
38225                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38226                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38227                 ]
38228             }, true);
38229         
38230         this.titleEl.enableDisplayMode();
38231         /** This region's title text element 
38232         * @type HTMLElement */
38233         this.titleTextEl = this.titleEl.dom.firstChild;
38234         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38235         /*
38236         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38237         this.closeBtn.enableDisplayMode();
38238         this.closeBtn.on("click", this.closeClicked, this);
38239         this.closeBtn.hide();
38240     */
38241         this.createBody(this.config);
38242         if(this.config.hideWhenEmpty){
38243             this.hide();
38244             this.on("paneladded", this.validateVisibility, this);
38245             this.on("panelremoved", this.validateVisibility, this);
38246         }
38247         if(this.autoScroll){
38248             this.bodyEl.setStyle("overflow", "auto");
38249         }else{
38250             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38251         }
38252         //if(c.titlebar !== false){
38253             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38254                 this.titleEl.hide();
38255             }else{
38256                 this.titleEl.show();
38257                 if(this.config.title){
38258                     this.titleTextEl.innerHTML = this.config.title;
38259                 }
38260             }
38261         //}
38262         if(this.config.collapsed){
38263             this.collapse(true);
38264         }
38265         if(this.config.hidden){
38266             this.hide();
38267         }
38268         
38269         if (this.unrendered_panels && this.unrendered_panels.length) {
38270             for (var i =0;i< this.unrendered_panels.length; i++) {
38271                 this.add(this.unrendered_panels[i]);
38272             }
38273             this.unrendered_panels = null;
38274             
38275         }
38276         
38277     },
38278     
38279     applyConfig : function(c)
38280     {
38281         /*
38282          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38283             var dh = Roo.DomHelper;
38284             if(c.titlebar !== false){
38285                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38286                 this.collapseBtn.on("click", this.collapse, this);
38287                 this.collapseBtn.enableDisplayMode();
38288                 /*
38289                 if(c.showPin === true || this.showPin){
38290                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38291                     this.stickBtn.enableDisplayMode();
38292                     this.stickBtn.on("click", this.expand, this);
38293                     this.stickBtn.hide();
38294                 }
38295                 
38296             }
38297             */
38298             /** This region's collapsed element
38299             * @type Roo.Element */
38300             /*
38301              *
38302             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38303                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38304             ]}, true);
38305             
38306             if(c.floatable !== false){
38307                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38308                this.collapsedEl.on("click", this.collapseClick, this);
38309             }
38310
38311             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38312                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38313                    id: "message", unselectable: "on", style:{"float":"left"}});
38314                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38315              }
38316             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38317             this.expandBtn.on("click", this.expand, this);
38318             
38319         }
38320         
38321         if(this.collapseBtn){
38322             this.collapseBtn.setVisible(c.collapsible == true);
38323         }
38324         
38325         this.cmargins = c.cmargins || this.cmargins ||
38326                          (this.position == "west" || this.position == "east" ?
38327                              {top: 0, left: 2, right:2, bottom: 0} :
38328                              {top: 2, left: 0, right:0, bottom: 2});
38329         */
38330         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38331         
38332         
38333         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38334         
38335         this.autoScroll = c.autoScroll || false;
38336         
38337         
38338        
38339         
38340         this.duration = c.duration || .30;
38341         this.slideDuration = c.slideDuration || .45;
38342         this.config = c;
38343        
38344     },
38345     /**
38346      * Returns true if this region is currently visible.
38347      * @return {Boolean}
38348      */
38349     isVisible : function(){
38350         return this.visible;
38351     },
38352
38353     /**
38354      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38355      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38356      */
38357     //setCollapsedTitle : function(title){
38358     //    title = title || "&#160;";
38359      //   if(this.collapsedTitleTextEl){
38360       //      this.collapsedTitleTextEl.innerHTML = title;
38361        // }
38362     //},
38363
38364     getBox : function(){
38365         var b;
38366       //  if(!this.collapsed){
38367             b = this.el.getBox(false, true);
38368        // }else{
38369           //  b = this.collapsedEl.getBox(false, true);
38370         //}
38371         return b;
38372     },
38373
38374     getMargins : function(){
38375         return this.margins;
38376         //return this.collapsed ? this.cmargins : this.margins;
38377     },
38378 /*
38379     highlight : function(){
38380         this.el.addClass("x-layout-panel-dragover");
38381     },
38382
38383     unhighlight : function(){
38384         this.el.removeClass("x-layout-panel-dragover");
38385     },
38386 */
38387     updateBox : function(box)
38388     {
38389         if (!this.bodyEl) {
38390             return; // not rendered yet..
38391         }
38392         
38393         this.box = box;
38394         if(!this.collapsed){
38395             this.el.dom.style.left = box.x + "px";
38396             this.el.dom.style.top = box.y + "px";
38397             this.updateBody(box.width, box.height);
38398         }else{
38399             this.collapsedEl.dom.style.left = box.x + "px";
38400             this.collapsedEl.dom.style.top = box.y + "px";
38401             this.collapsedEl.setSize(box.width, box.height);
38402         }
38403         if(this.tabs){
38404             this.tabs.autoSizeTabs();
38405         }
38406     },
38407
38408     updateBody : function(w, h)
38409     {
38410         if(w !== null){
38411             this.el.setWidth(w);
38412             w -= this.el.getBorderWidth("rl");
38413             if(this.config.adjustments){
38414                 w += this.config.adjustments[0];
38415             }
38416         }
38417         if(h !== null && h > 0){
38418             this.el.setHeight(h);
38419             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38420             h -= this.el.getBorderWidth("tb");
38421             if(this.config.adjustments){
38422                 h += this.config.adjustments[1];
38423             }
38424             this.bodyEl.setHeight(h);
38425             if(this.tabs){
38426                 h = this.tabs.syncHeight(h);
38427             }
38428         }
38429         if(this.panelSize){
38430             w = w !== null ? w : this.panelSize.width;
38431             h = h !== null ? h : this.panelSize.height;
38432         }
38433         if(this.activePanel){
38434             var el = this.activePanel.getEl();
38435             w = w !== null ? w : el.getWidth();
38436             h = h !== null ? h : el.getHeight();
38437             this.panelSize = {width: w, height: h};
38438             this.activePanel.setSize(w, h);
38439         }
38440         if(Roo.isIE && this.tabs){
38441             this.tabs.el.repaint();
38442         }
38443     },
38444
38445     /**
38446      * Returns the container element for this region.
38447      * @return {Roo.Element}
38448      */
38449     getEl : function(){
38450         return this.el;
38451     },
38452
38453     /**
38454      * Hides this region.
38455      */
38456     hide : function(){
38457         //if(!this.collapsed){
38458             this.el.dom.style.left = "-2000px";
38459             this.el.hide();
38460         //}else{
38461          //   this.collapsedEl.dom.style.left = "-2000px";
38462          //   this.collapsedEl.hide();
38463        // }
38464         this.visible = false;
38465         this.fireEvent("visibilitychange", this, false);
38466     },
38467
38468     /**
38469      * Shows this region if it was previously hidden.
38470      */
38471     show : function(){
38472         //if(!this.collapsed){
38473             this.el.show();
38474         //}else{
38475         //    this.collapsedEl.show();
38476        // }
38477         this.visible = true;
38478         this.fireEvent("visibilitychange", this, true);
38479     },
38480 /*
38481     closeClicked : function(){
38482         if(this.activePanel){
38483             this.remove(this.activePanel);
38484         }
38485     },
38486
38487     collapseClick : function(e){
38488         if(this.isSlid){
38489            e.stopPropagation();
38490            this.slideIn();
38491         }else{
38492            e.stopPropagation();
38493            this.slideOut();
38494         }
38495     },
38496 */
38497     /**
38498      * Collapses this region.
38499      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38500      */
38501     /*
38502     collapse : function(skipAnim, skipCheck = false){
38503         if(this.collapsed) {
38504             return;
38505         }
38506         
38507         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38508             
38509             this.collapsed = true;
38510             if(this.split){
38511                 this.split.el.hide();
38512             }
38513             if(this.config.animate && skipAnim !== true){
38514                 this.fireEvent("invalidated", this);
38515                 this.animateCollapse();
38516             }else{
38517                 this.el.setLocation(-20000,-20000);
38518                 this.el.hide();
38519                 this.collapsedEl.show();
38520                 this.fireEvent("collapsed", this);
38521                 this.fireEvent("invalidated", this);
38522             }
38523         }
38524         
38525     },
38526 */
38527     animateCollapse : function(){
38528         // overridden
38529     },
38530
38531     /**
38532      * Expands this region if it was previously collapsed.
38533      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38534      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38535      */
38536     /*
38537     expand : function(e, skipAnim){
38538         if(e) {
38539             e.stopPropagation();
38540         }
38541         if(!this.collapsed || this.el.hasActiveFx()) {
38542             return;
38543         }
38544         if(this.isSlid){
38545             this.afterSlideIn();
38546             skipAnim = true;
38547         }
38548         this.collapsed = false;
38549         if(this.config.animate && skipAnim !== true){
38550             this.animateExpand();
38551         }else{
38552             this.el.show();
38553             if(this.split){
38554                 this.split.el.show();
38555             }
38556             this.collapsedEl.setLocation(-2000,-2000);
38557             this.collapsedEl.hide();
38558             this.fireEvent("invalidated", this);
38559             this.fireEvent("expanded", this);
38560         }
38561     },
38562 */
38563     animateExpand : function(){
38564         // overridden
38565     },
38566
38567     initTabs : function()
38568     {
38569         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38570         
38571         var ts = new Roo.bootstrap.panel.Tabs({
38572             el: this.bodyEl.dom,
38573             region : this,
38574             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38575             disableTooltips: this.config.disableTabTips,
38576             toolbar : this.config.toolbar
38577         });
38578         
38579         if(this.config.hideTabs){
38580             ts.stripWrap.setDisplayed(false);
38581         }
38582         this.tabs = ts;
38583         ts.resizeTabs = this.config.resizeTabs === true;
38584         ts.minTabWidth = this.config.minTabWidth || 40;
38585         ts.maxTabWidth = this.config.maxTabWidth || 250;
38586         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38587         ts.monitorResize = false;
38588         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38589         ts.bodyEl.addClass('roo-layout-tabs-body');
38590         this.panels.each(this.initPanelAsTab, this);
38591     },
38592
38593     initPanelAsTab : function(panel){
38594         var ti = this.tabs.addTab(
38595             panel.getEl().id,
38596             panel.getTitle(),
38597             null,
38598             this.config.closeOnTab && panel.isClosable(),
38599             panel.tpl
38600         );
38601         if(panel.tabTip !== undefined){
38602             ti.setTooltip(panel.tabTip);
38603         }
38604         ti.on("activate", function(){
38605               this.setActivePanel(panel);
38606         }, this);
38607         
38608         if(this.config.closeOnTab){
38609             ti.on("beforeclose", function(t, e){
38610                 e.cancel = true;
38611                 this.remove(panel);
38612             }, this);
38613         }
38614         
38615         panel.tabItem = ti;
38616         
38617         return ti;
38618     },
38619
38620     updatePanelTitle : function(panel, title)
38621     {
38622         if(this.activePanel == panel){
38623             this.updateTitle(title);
38624         }
38625         if(this.tabs){
38626             var ti = this.tabs.getTab(panel.getEl().id);
38627             ti.setText(title);
38628             if(panel.tabTip !== undefined){
38629                 ti.setTooltip(panel.tabTip);
38630             }
38631         }
38632     },
38633
38634     updateTitle : function(title){
38635         if(this.titleTextEl && !this.config.title){
38636             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38637         }
38638     },
38639
38640     setActivePanel : function(panel)
38641     {
38642         panel = this.getPanel(panel);
38643         if(this.activePanel && this.activePanel != panel){
38644             if(this.activePanel.setActiveState(false) === false){
38645                 return;
38646             }
38647         }
38648         this.activePanel = panel;
38649         panel.setActiveState(true);
38650         if(this.panelSize){
38651             panel.setSize(this.panelSize.width, this.panelSize.height);
38652         }
38653         if(this.closeBtn){
38654             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38655         }
38656         this.updateTitle(panel.getTitle());
38657         if(this.tabs){
38658             this.fireEvent("invalidated", this);
38659         }
38660         this.fireEvent("panelactivated", this, panel);
38661     },
38662
38663     /**
38664      * Shows the specified panel.
38665      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38666      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38667      */
38668     showPanel : function(panel)
38669     {
38670         panel = this.getPanel(panel);
38671         if(panel){
38672             if(this.tabs){
38673                 var tab = this.tabs.getTab(panel.getEl().id);
38674                 if(tab.isHidden()){
38675                     this.tabs.unhideTab(tab.id);
38676                 }
38677                 tab.activate();
38678             }else{
38679                 this.setActivePanel(panel);
38680             }
38681         }
38682         return panel;
38683     },
38684
38685     /**
38686      * Get the active panel for this region.
38687      * @return {Roo.ContentPanel} The active panel or null
38688      */
38689     getActivePanel : function(){
38690         return this.activePanel;
38691     },
38692
38693     validateVisibility : function(){
38694         if(this.panels.getCount() < 1){
38695             this.updateTitle("&#160;");
38696             this.closeBtn.hide();
38697             this.hide();
38698         }else{
38699             if(!this.isVisible()){
38700                 this.show();
38701             }
38702         }
38703     },
38704
38705     /**
38706      * Adds the passed ContentPanel(s) to this region.
38707      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38708      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38709      */
38710     add : function(panel)
38711     {
38712         if(arguments.length > 1){
38713             for(var i = 0, len = arguments.length; i < len; i++) {
38714                 this.add(arguments[i]);
38715             }
38716             return null;
38717         }
38718         
38719         // if we have not been rendered yet, then we can not really do much of this..
38720         if (!this.bodyEl) {
38721             this.unrendered_panels.push(panel);
38722             return panel;
38723         }
38724         
38725         
38726         
38727         
38728         if(this.hasPanel(panel)){
38729             this.showPanel(panel);
38730             return panel;
38731         }
38732         panel.setRegion(this);
38733         this.panels.add(panel);
38734        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38735             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38736             // and hide them... ???
38737             this.bodyEl.dom.appendChild(panel.getEl().dom);
38738             if(panel.background !== true){
38739                 this.setActivePanel(panel);
38740             }
38741             this.fireEvent("paneladded", this, panel);
38742             return panel;
38743         }
38744         */
38745         if(!this.tabs){
38746             this.initTabs();
38747         }else{
38748             this.initPanelAsTab(panel);
38749         }
38750         
38751         
38752         if(panel.background !== true){
38753             this.tabs.activate(panel.getEl().id);
38754         }
38755         this.fireEvent("paneladded", this, panel);
38756         return panel;
38757     },
38758
38759     /**
38760      * Hides the tab for the specified panel.
38761      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38762      */
38763     hidePanel : function(panel){
38764         if(this.tabs && (panel = this.getPanel(panel))){
38765             this.tabs.hideTab(panel.getEl().id);
38766         }
38767     },
38768
38769     /**
38770      * Unhides the tab for a previously hidden panel.
38771      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38772      */
38773     unhidePanel : function(panel){
38774         if(this.tabs && (panel = this.getPanel(panel))){
38775             this.tabs.unhideTab(panel.getEl().id);
38776         }
38777     },
38778
38779     clearPanels : function(){
38780         while(this.panels.getCount() > 0){
38781              this.remove(this.panels.first());
38782         }
38783     },
38784
38785     /**
38786      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38787      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38788      * @param {Boolean} preservePanel Overrides the config preservePanel option
38789      * @return {Roo.ContentPanel} The panel that was removed
38790      */
38791     remove : function(panel, preservePanel)
38792     {
38793         panel = this.getPanel(panel);
38794         if(!panel){
38795             return null;
38796         }
38797         var e = {};
38798         this.fireEvent("beforeremove", this, panel, e);
38799         if(e.cancel === true){
38800             return null;
38801         }
38802         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38803         var panelId = panel.getId();
38804         this.panels.removeKey(panelId);
38805         if(preservePanel){
38806             document.body.appendChild(panel.getEl().dom);
38807         }
38808         if(this.tabs){
38809             this.tabs.removeTab(panel.getEl().id);
38810         }else if (!preservePanel){
38811             this.bodyEl.dom.removeChild(panel.getEl().dom);
38812         }
38813         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38814             var p = this.panels.first();
38815             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38816             tempEl.appendChild(p.getEl().dom);
38817             this.bodyEl.update("");
38818             this.bodyEl.dom.appendChild(p.getEl().dom);
38819             tempEl = null;
38820             this.updateTitle(p.getTitle());
38821             this.tabs = null;
38822             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38823             this.setActivePanel(p);
38824         }
38825         panel.setRegion(null);
38826         if(this.activePanel == panel){
38827             this.activePanel = null;
38828         }
38829         if(this.config.autoDestroy !== false && preservePanel !== true){
38830             try{panel.destroy();}catch(e){}
38831         }
38832         this.fireEvent("panelremoved", this, panel);
38833         return panel;
38834     },
38835
38836     /**
38837      * Returns the TabPanel component used by this region
38838      * @return {Roo.TabPanel}
38839      */
38840     getTabs : function(){
38841         return this.tabs;
38842     },
38843
38844     createTool : function(parentEl, className){
38845         var btn = Roo.DomHelper.append(parentEl, {
38846             tag: "div",
38847             cls: "x-layout-tools-button",
38848             children: [ {
38849                 tag: "div",
38850                 cls: "roo-layout-tools-button-inner " + className,
38851                 html: "&#160;"
38852             }]
38853         }, true);
38854         btn.addClassOnOver("roo-layout-tools-button-over");
38855         return btn;
38856     }
38857 });/*
38858  * Based on:
38859  * Ext JS Library 1.1.1
38860  * Copyright(c) 2006-2007, Ext JS, LLC.
38861  *
38862  * Originally Released Under LGPL - original licence link has changed is not relivant.
38863  *
38864  * Fork - LGPL
38865  * <script type="text/javascript">
38866  */
38867  
38868
38869
38870 /**
38871  * @class Roo.SplitLayoutRegion
38872  * @extends Roo.LayoutRegion
38873  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38874  */
38875 Roo.bootstrap.layout.Split = function(config){
38876     this.cursor = config.cursor;
38877     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38878 };
38879
38880 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38881 {
38882     splitTip : "Drag to resize.",
38883     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38884     useSplitTips : false,
38885
38886     applyConfig : function(config){
38887         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38888     },
38889     
38890     onRender : function(ctr,pos) {
38891         
38892         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38893         if(!this.config.split){
38894             return;
38895         }
38896         if(!this.split){
38897             
38898             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38899                             tag: "div",
38900                             id: this.el.id + "-split",
38901                             cls: "roo-layout-split roo-layout-split-"+this.position,
38902                             html: "&#160;"
38903             });
38904             /** The SplitBar for this region 
38905             * @type Roo.SplitBar */
38906             // does not exist yet...
38907             Roo.log([this.position, this.orientation]);
38908             
38909             this.split = new Roo.bootstrap.SplitBar({
38910                 dragElement : splitEl,
38911                 resizingElement: this.el,
38912                 orientation : this.orientation
38913             });
38914             
38915             this.split.on("moved", this.onSplitMove, this);
38916             this.split.useShim = this.config.useShim === true;
38917             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38918             if(this.useSplitTips){
38919                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38920             }
38921             //if(config.collapsible){
38922             //    this.split.el.on("dblclick", this.collapse,  this);
38923             //}
38924         }
38925         if(typeof this.config.minSize != "undefined"){
38926             this.split.minSize = this.config.minSize;
38927         }
38928         if(typeof this.config.maxSize != "undefined"){
38929             this.split.maxSize = this.config.maxSize;
38930         }
38931         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38932             this.hideSplitter();
38933         }
38934         
38935     },
38936
38937     getHMaxSize : function(){
38938          var cmax = this.config.maxSize || 10000;
38939          var center = this.mgr.getRegion("center");
38940          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38941     },
38942
38943     getVMaxSize : function(){
38944          var cmax = this.config.maxSize || 10000;
38945          var center = this.mgr.getRegion("center");
38946          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38947     },
38948
38949     onSplitMove : function(split, newSize){
38950         this.fireEvent("resized", this, newSize);
38951     },
38952     
38953     /** 
38954      * Returns the {@link Roo.SplitBar} for this region.
38955      * @return {Roo.SplitBar}
38956      */
38957     getSplitBar : function(){
38958         return this.split;
38959     },
38960     
38961     hide : function(){
38962         this.hideSplitter();
38963         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38964     },
38965
38966     hideSplitter : function(){
38967         if(this.split){
38968             this.split.el.setLocation(-2000,-2000);
38969             this.split.el.hide();
38970         }
38971     },
38972
38973     show : function(){
38974         if(this.split){
38975             this.split.el.show();
38976         }
38977         Roo.bootstrap.layout.Split.superclass.show.call(this);
38978     },
38979     
38980     beforeSlide: function(){
38981         if(Roo.isGecko){// firefox overflow auto bug workaround
38982             this.bodyEl.clip();
38983             if(this.tabs) {
38984                 this.tabs.bodyEl.clip();
38985             }
38986             if(this.activePanel){
38987                 this.activePanel.getEl().clip();
38988                 
38989                 if(this.activePanel.beforeSlide){
38990                     this.activePanel.beforeSlide();
38991                 }
38992             }
38993         }
38994     },
38995     
38996     afterSlide : function(){
38997         if(Roo.isGecko){// firefox overflow auto bug workaround
38998             this.bodyEl.unclip();
38999             if(this.tabs) {
39000                 this.tabs.bodyEl.unclip();
39001             }
39002             if(this.activePanel){
39003                 this.activePanel.getEl().unclip();
39004                 if(this.activePanel.afterSlide){
39005                     this.activePanel.afterSlide();
39006                 }
39007             }
39008         }
39009     },
39010
39011     initAutoHide : function(){
39012         if(this.autoHide !== false){
39013             if(!this.autoHideHd){
39014                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39015                 this.autoHideHd = {
39016                     "mouseout": function(e){
39017                         if(!e.within(this.el, true)){
39018                             st.delay(500);
39019                         }
39020                     },
39021                     "mouseover" : function(e){
39022                         st.cancel();
39023                     },
39024                     scope : this
39025                 };
39026             }
39027             this.el.on(this.autoHideHd);
39028         }
39029     },
39030
39031     clearAutoHide : function(){
39032         if(this.autoHide !== false){
39033             this.el.un("mouseout", this.autoHideHd.mouseout);
39034             this.el.un("mouseover", this.autoHideHd.mouseover);
39035         }
39036     },
39037
39038     clearMonitor : function(){
39039         Roo.get(document).un("click", this.slideInIf, this);
39040     },
39041
39042     // these names are backwards but not changed for compat
39043     slideOut : function(){
39044         if(this.isSlid || this.el.hasActiveFx()){
39045             return;
39046         }
39047         this.isSlid = true;
39048         if(this.collapseBtn){
39049             this.collapseBtn.hide();
39050         }
39051         this.closeBtnState = this.closeBtn.getStyle('display');
39052         this.closeBtn.hide();
39053         if(this.stickBtn){
39054             this.stickBtn.show();
39055         }
39056         this.el.show();
39057         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39058         this.beforeSlide();
39059         this.el.setStyle("z-index", 10001);
39060         this.el.slideIn(this.getSlideAnchor(), {
39061             callback: function(){
39062                 this.afterSlide();
39063                 this.initAutoHide();
39064                 Roo.get(document).on("click", this.slideInIf, this);
39065                 this.fireEvent("slideshow", this);
39066             },
39067             scope: this,
39068             block: true
39069         });
39070     },
39071
39072     afterSlideIn : function(){
39073         this.clearAutoHide();
39074         this.isSlid = false;
39075         this.clearMonitor();
39076         this.el.setStyle("z-index", "");
39077         if(this.collapseBtn){
39078             this.collapseBtn.show();
39079         }
39080         this.closeBtn.setStyle('display', this.closeBtnState);
39081         if(this.stickBtn){
39082             this.stickBtn.hide();
39083         }
39084         this.fireEvent("slidehide", this);
39085     },
39086
39087     slideIn : function(cb){
39088         if(!this.isSlid || this.el.hasActiveFx()){
39089             Roo.callback(cb);
39090             return;
39091         }
39092         this.isSlid = false;
39093         this.beforeSlide();
39094         this.el.slideOut(this.getSlideAnchor(), {
39095             callback: function(){
39096                 this.el.setLeftTop(-10000, -10000);
39097                 this.afterSlide();
39098                 this.afterSlideIn();
39099                 Roo.callback(cb);
39100             },
39101             scope: this,
39102             block: true
39103         });
39104     },
39105     
39106     slideInIf : function(e){
39107         if(!e.within(this.el)){
39108             this.slideIn();
39109         }
39110     },
39111
39112     animateCollapse : function(){
39113         this.beforeSlide();
39114         this.el.setStyle("z-index", 20000);
39115         var anchor = this.getSlideAnchor();
39116         this.el.slideOut(anchor, {
39117             callback : function(){
39118                 this.el.setStyle("z-index", "");
39119                 this.collapsedEl.slideIn(anchor, {duration:.3});
39120                 this.afterSlide();
39121                 this.el.setLocation(-10000,-10000);
39122                 this.el.hide();
39123                 this.fireEvent("collapsed", this);
39124             },
39125             scope: this,
39126             block: true
39127         });
39128     },
39129
39130     animateExpand : function(){
39131         this.beforeSlide();
39132         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39133         this.el.setStyle("z-index", 20000);
39134         this.collapsedEl.hide({
39135             duration:.1
39136         });
39137         this.el.slideIn(this.getSlideAnchor(), {
39138             callback : function(){
39139                 this.el.setStyle("z-index", "");
39140                 this.afterSlide();
39141                 if(this.split){
39142                     this.split.el.show();
39143                 }
39144                 this.fireEvent("invalidated", this);
39145                 this.fireEvent("expanded", this);
39146             },
39147             scope: this,
39148             block: true
39149         });
39150     },
39151
39152     anchors : {
39153         "west" : "left",
39154         "east" : "right",
39155         "north" : "top",
39156         "south" : "bottom"
39157     },
39158
39159     sanchors : {
39160         "west" : "l",
39161         "east" : "r",
39162         "north" : "t",
39163         "south" : "b"
39164     },
39165
39166     canchors : {
39167         "west" : "tl-tr",
39168         "east" : "tr-tl",
39169         "north" : "tl-bl",
39170         "south" : "bl-tl"
39171     },
39172
39173     getAnchor : function(){
39174         return this.anchors[this.position];
39175     },
39176
39177     getCollapseAnchor : function(){
39178         return this.canchors[this.position];
39179     },
39180
39181     getSlideAnchor : function(){
39182         return this.sanchors[this.position];
39183     },
39184
39185     getAlignAdj : function(){
39186         var cm = this.cmargins;
39187         switch(this.position){
39188             case "west":
39189                 return [0, 0];
39190             break;
39191             case "east":
39192                 return [0, 0];
39193             break;
39194             case "north":
39195                 return [0, 0];
39196             break;
39197             case "south":
39198                 return [0, 0];
39199             break;
39200         }
39201     },
39202
39203     getExpandAdj : function(){
39204         var c = this.collapsedEl, cm = this.cmargins;
39205         switch(this.position){
39206             case "west":
39207                 return [-(cm.right+c.getWidth()+cm.left), 0];
39208             break;
39209             case "east":
39210                 return [cm.right+c.getWidth()+cm.left, 0];
39211             break;
39212             case "north":
39213                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39214             break;
39215             case "south":
39216                 return [0, cm.top+cm.bottom+c.getHeight()];
39217             break;
39218         }
39219     }
39220 });/*
39221  * Based on:
39222  * Ext JS Library 1.1.1
39223  * Copyright(c) 2006-2007, Ext JS, LLC.
39224  *
39225  * Originally Released Under LGPL - original licence link has changed is not relivant.
39226  *
39227  * Fork - LGPL
39228  * <script type="text/javascript">
39229  */
39230 /*
39231  * These classes are private internal classes
39232  */
39233 Roo.bootstrap.layout.Center = function(config){
39234     config.region = "center";
39235     Roo.bootstrap.layout.Region.call(this, config);
39236     this.visible = true;
39237     this.minWidth = config.minWidth || 20;
39238     this.minHeight = config.minHeight || 20;
39239 };
39240
39241 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39242     hide : function(){
39243         // center panel can't be hidden
39244     },
39245     
39246     show : function(){
39247         // center panel can't be hidden
39248     },
39249     
39250     getMinWidth: function(){
39251         return this.minWidth;
39252     },
39253     
39254     getMinHeight: function(){
39255         return this.minHeight;
39256     }
39257 });
39258
39259
39260
39261
39262  
39263
39264
39265
39266
39267
39268
39269 Roo.bootstrap.layout.North = function(config)
39270 {
39271     config.region = 'north';
39272     config.cursor = 'n-resize';
39273     
39274     Roo.bootstrap.layout.Split.call(this, config);
39275     
39276     
39277     if(this.split){
39278         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39279         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39280         this.split.el.addClass("roo-layout-split-v");
39281     }
39282     //var size = config.initialSize || config.height;
39283     //if(this.el && typeof size != "undefined"){
39284     //    this.el.setHeight(size);
39285     //}
39286 };
39287 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39288 {
39289     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39290      
39291      
39292     onRender : function(ctr, pos)
39293     {
39294         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39295         var size = this.config.initialSize || this.config.height;
39296         if(this.el && typeof size != "undefined"){
39297             this.el.setHeight(size);
39298         }
39299     
39300     },
39301     
39302     getBox : function(){
39303         if(this.collapsed){
39304             return this.collapsedEl.getBox();
39305         }
39306         var box = this.el.getBox();
39307         if(this.split){
39308             box.height += this.split.el.getHeight();
39309         }
39310         return box;
39311     },
39312     
39313     updateBox : function(box){
39314         if(this.split && !this.collapsed){
39315             box.height -= this.split.el.getHeight();
39316             this.split.el.setLeft(box.x);
39317             this.split.el.setTop(box.y+box.height);
39318             this.split.el.setWidth(box.width);
39319         }
39320         if(this.collapsed){
39321             this.updateBody(box.width, null);
39322         }
39323         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39324     }
39325 });
39326
39327
39328
39329
39330
39331 Roo.bootstrap.layout.South = function(config){
39332     config.region = 'south';
39333     config.cursor = 's-resize';
39334     Roo.bootstrap.layout.Split.call(this, config);
39335     if(this.split){
39336         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39337         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39338         this.split.el.addClass("roo-layout-split-v");
39339     }
39340     
39341 };
39342
39343 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39344     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39345     
39346     onRender : function(ctr, pos)
39347     {
39348         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39349         var size = this.config.initialSize || this.config.height;
39350         if(this.el && typeof size != "undefined"){
39351             this.el.setHeight(size);
39352         }
39353     
39354     },
39355     
39356     getBox : function(){
39357         if(this.collapsed){
39358             return this.collapsedEl.getBox();
39359         }
39360         var box = this.el.getBox();
39361         if(this.split){
39362             var sh = this.split.el.getHeight();
39363             box.height += sh;
39364             box.y -= sh;
39365         }
39366         return box;
39367     },
39368     
39369     updateBox : function(box){
39370         if(this.split && !this.collapsed){
39371             var sh = this.split.el.getHeight();
39372             box.height -= sh;
39373             box.y += sh;
39374             this.split.el.setLeft(box.x);
39375             this.split.el.setTop(box.y-sh);
39376             this.split.el.setWidth(box.width);
39377         }
39378         if(this.collapsed){
39379             this.updateBody(box.width, null);
39380         }
39381         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39382     }
39383 });
39384
39385 Roo.bootstrap.layout.East = function(config){
39386     config.region = "east";
39387     config.cursor = "e-resize";
39388     Roo.bootstrap.layout.Split.call(this, config);
39389     if(this.split){
39390         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39391         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39392         this.split.el.addClass("roo-layout-split-h");
39393     }
39394     
39395 };
39396 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39397     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39398     
39399     onRender : function(ctr, pos)
39400     {
39401         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39402         var size = this.config.initialSize || this.config.width;
39403         if(this.el && typeof size != "undefined"){
39404             this.el.setWidth(size);
39405         }
39406     
39407     },
39408     
39409     getBox : function(){
39410         if(this.collapsed){
39411             return this.collapsedEl.getBox();
39412         }
39413         var box = this.el.getBox();
39414         if(this.split){
39415             var sw = this.split.el.getWidth();
39416             box.width += sw;
39417             box.x -= sw;
39418         }
39419         return box;
39420     },
39421
39422     updateBox : function(box){
39423         if(this.split && !this.collapsed){
39424             var sw = this.split.el.getWidth();
39425             box.width -= sw;
39426             this.split.el.setLeft(box.x);
39427             this.split.el.setTop(box.y);
39428             this.split.el.setHeight(box.height);
39429             box.x += sw;
39430         }
39431         if(this.collapsed){
39432             this.updateBody(null, box.height);
39433         }
39434         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39435     }
39436 });
39437
39438 Roo.bootstrap.layout.West = function(config){
39439     config.region = "west";
39440     config.cursor = "w-resize";
39441     
39442     Roo.bootstrap.layout.Split.call(this, config);
39443     if(this.split){
39444         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39445         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39446         this.split.el.addClass("roo-layout-split-h");
39447     }
39448     
39449 };
39450 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39451     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39452     
39453     onRender: function(ctr, pos)
39454     {
39455         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39456         var size = this.config.initialSize || this.config.width;
39457         if(typeof size != "undefined"){
39458             this.el.setWidth(size);
39459         }
39460     },
39461     
39462     getBox : function(){
39463         if(this.collapsed){
39464             return this.collapsedEl.getBox();
39465         }
39466         var box = this.el.getBox();
39467         if (box.width == 0) {
39468             box.width = this.config.width; // kludge?
39469         }
39470         if(this.split){
39471             box.width += this.split.el.getWidth();
39472         }
39473         return box;
39474     },
39475     
39476     updateBox : function(box){
39477         if(this.split && !this.collapsed){
39478             var sw = this.split.el.getWidth();
39479             box.width -= sw;
39480             this.split.el.setLeft(box.x+box.width);
39481             this.split.el.setTop(box.y);
39482             this.split.el.setHeight(box.height);
39483         }
39484         if(this.collapsed){
39485             this.updateBody(null, box.height);
39486         }
39487         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39488     }
39489 });Roo.namespace("Roo.bootstrap.panel");/*
39490  * Based on:
39491  * Ext JS Library 1.1.1
39492  * Copyright(c) 2006-2007, Ext JS, LLC.
39493  *
39494  * Originally Released Under LGPL - original licence link has changed is not relivant.
39495  *
39496  * Fork - LGPL
39497  * <script type="text/javascript">
39498  */
39499 /**
39500  * @class Roo.ContentPanel
39501  * @extends Roo.util.Observable
39502  * A basic ContentPanel element.
39503  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39504  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39505  * @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
39506  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39507  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39508  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39509  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39510  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39511  * @cfg {String} title          The title for this panel
39512  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39513  * @cfg {String} url            Calls {@link #setUrl} with this value
39514  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39515  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39516  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39517  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39518  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39519  * @cfg {Boolean} badges render the badges
39520  * @cfg {String} cls  extra classes to use  
39521  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39522
39523  * @constructor
39524  * Create a new ContentPanel.
39525  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39526  * @param {String/Object} config A string to set only the title or a config object
39527  * @param {String} content (optional) Set the HTML content for this panel
39528  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39529  */
39530 Roo.bootstrap.panel.Content = function( config){
39531     
39532     this.tpl = config.tpl || false;
39533     
39534     var el = config.el;
39535     var content = config.content;
39536
39537     if(config.autoCreate){ // xtype is available if this is called from factory
39538         el = Roo.id();
39539     }
39540     this.el = Roo.get(el);
39541     if(!this.el && config && config.autoCreate){
39542         if(typeof config.autoCreate == "object"){
39543             if(!config.autoCreate.id){
39544                 config.autoCreate.id = config.id||el;
39545             }
39546             this.el = Roo.DomHelper.append(document.body,
39547                         config.autoCreate, true);
39548         }else{
39549             var elcfg =  {
39550                 tag: "div",
39551                 cls: (config.cls || '') +
39552                     (config.background ? ' bg-' + config.background : '') +
39553                     " roo-layout-inactive-content",
39554                 id: config.id||el
39555             };
39556             if (config.iframe) {
39557                 elcfg.cn = [
39558                     {
39559                         tag : 'iframe',
39560                         style : 'border: 0px',
39561                         src : 'about:blank'
39562                     }
39563                 ];
39564             }
39565               
39566             if (config.html) {
39567                 elcfg.html = config.html;
39568                 
39569             }
39570                         
39571             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39572             if (config.iframe) {
39573                 this.iframeEl = this.el.select('iframe',true).first();
39574             }
39575             
39576         }
39577     } 
39578     this.closable = false;
39579     this.loaded = false;
39580     this.active = false;
39581    
39582       
39583     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39584         
39585         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39586         
39587         this.wrapEl = this.el; //this.el.wrap();
39588         var ti = [];
39589         if (config.toolbar.items) {
39590             ti = config.toolbar.items ;
39591             delete config.toolbar.items ;
39592         }
39593         
39594         var nitems = [];
39595         this.toolbar.render(this.wrapEl, 'before');
39596         for(var i =0;i < ti.length;i++) {
39597           //  Roo.log(['add child', items[i]]);
39598             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39599         }
39600         this.toolbar.items = nitems;
39601         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39602         delete config.toolbar;
39603         
39604     }
39605     /*
39606     // xtype created footer. - not sure if will work as we normally have to render first..
39607     if (this.footer && !this.footer.el && this.footer.xtype) {
39608         if (!this.wrapEl) {
39609             this.wrapEl = this.el.wrap();
39610         }
39611     
39612         this.footer.container = this.wrapEl.createChild();
39613          
39614         this.footer = Roo.factory(this.footer, Roo);
39615         
39616     }
39617     */
39618     
39619      if(typeof config == "string"){
39620         this.title = config;
39621     }else{
39622         Roo.apply(this, config);
39623     }
39624     
39625     if(this.resizeEl){
39626         this.resizeEl = Roo.get(this.resizeEl, true);
39627     }else{
39628         this.resizeEl = this.el;
39629     }
39630     // handle view.xtype
39631     
39632  
39633     
39634     
39635     this.addEvents({
39636         /**
39637          * @event activate
39638          * Fires when this panel is activated. 
39639          * @param {Roo.ContentPanel} this
39640          */
39641         "activate" : true,
39642         /**
39643          * @event deactivate
39644          * Fires when this panel is activated. 
39645          * @param {Roo.ContentPanel} this
39646          */
39647         "deactivate" : true,
39648
39649         /**
39650          * @event resize
39651          * Fires when this panel is resized if fitToFrame is true.
39652          * @param {Roo.ContentPanel} this
39653          * @param {Number} width The width after any component adjustments
39654          * @param {Number} height The height after any component adjustments
39655          */
39656         "resize" : true,
39657         
39658          /**
39659          * @event render
39660          * Fires when this tab is created
39661          * @param {Roo.ContentPanel} this
39662          */
39663         "render" : true
39664         
39665         
39666         
39667     });
39668     
39669
39670     
39671     
39672     if(this.autoScroll && !this.iframe){
39673         this.resizeEl.setStyle("overflow", "auto");
39674     } else {
39675         // fix randome scrolling
39676         //this.el.on('scroll', function() {
39677         //    Roo.log('fix random scolling');
39678         //    this.scrollTo('top',0); 
39679         //});
39680     }
39681     content = content || this.content;
39682     if(content){
39683         this.setContent(content);
39684     }
39685     if(config && config.url){
39686         this.setUrl(this.url, this.params, this.loadOnce);
39687     }
39688     
39689     
39690     
39691     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39692     
39693     if (this.view && typeof(this.view.xtype) != 'undefined') {
39694         this.view.el = this.el.appendChild(document.createElement("div"));
39695         this.view = Roo.factory(this.view); 
39696         this.view.render  &&  this.view.render(false, '');  
39697     }
39698     
39699     
39700     this.fireEvent('render', this);
39701 };
39702
39703 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39704     
39705     cls : '',
39706     background : '',
39707     
39708     tabTip : '',
39709     
39710     iframe : false,
39711     iframeEl : false,
39712     
39713     setRegion : function(region){
39714         this.region = region;
39715         this.setActiveClass(region && !this.background);
39716     },
39717     
39718     
39719     setActiveClass: function(state)
39720     {
39721         if(state){
39722            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39723            this.el.setStyle('position','relative');
39724         }else{
39725            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39726            this.el.setStyle('position', 'absolute');
39727         } 
39728     },
39729     
39730     /**
39731      * Returns the toolbar for this Panel if one was configured. 
39732      * @return {Roo.Toolbar} 
39733      */
39734     getToolbar : function(){
39735         return this.toolbar;
39736     },
39737     
39738     setActiveState : function(active)
39739     {
39740         this.active = active;
39741         this.setActiveClass(active);
39742         if(!active){
39743             if(this.fireEvent("deactivate", this) === false){
39744                 return false;
39745             }
39746             return true;
39747         }
39748         this.fireEvent("activate", this);
39749         return true;
39750     },
39751     /**
39752      * Updates this panel's element (not for iframe)
39753      * @param {String} content The new content
39754      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39755     */
39756     setContent : function(content, loadScripts){
39757         if (this.iframe) {
39758             return;
39759         }
39760         
39761         this.el.update(content, loadScripts);
39762     },
39763
39764     ignoreResize : function(w, h){
39765         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39766             return true;
39767         }else{
39768             this.lastSize = {width: w, height: h};
39769             return false;
39770         }
39771     },
39772     /**
39773      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39774      * @return {Roo.UpdateManager} The UpdateManager
39775      */
39776     getUpdateManager : function(){
39777         if (this.iframe) {
39778             return false;
39779         }
39780         return this.el.getUpdateManager();
39781     },
39782      /**
39783      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39784      * Does not work with IFRAME contents
39785      * @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:
39786 <pre><code>
39787 panel.load({
39788     url: "your-url.php",
39789     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39790     callback: yourFunction,
39791     scope: yourObject, //(optional scope)
39792     discardUrl: false,
39793     nocache: false,
39794     text: "Loading...",
39795     timeout: 30,
39796     scripts: false
39797 });
39798 </code></pre>
39799      
39800      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39801      * 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.
39802      * @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}
39803      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39804      * @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.
39805      * @return {Roo.ContentPanel} this
39806      */
39807     load : function(){
39808         
39809         if (this.iframe) {
39810             return this;
39811         }
39812         
39813         var um = this.el.getUpdateManager();
39814         um.update.apply(um, arguments);
39815         return this;
39816     },
39817
39818
39819     /**
39820      * 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.
39821      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39822      * @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)
39823      * @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)
39824      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39825      */
39826     setUrl : function(url, params, loadOnce){
39827         if (this.iframe) {
39828             this.iframeEl.dom.src = url;
39829             return false;
39830         }
39831         
39832         if(this.refreshDelegate){
39833             this.removeListener("activate", this.refreshDelegate);
39834         }
39835         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39836         this.on("activate", this.refreshDelegate);
39837         return this.el.getUpdateManager();
39838     },
39839     
39840     _handleRefresh : function(url, params, loadOnce){
39841         if(!loadOnce || !this.loaded){
39842             var updater = this.el.getUpdateManager();
39843             updater.update(url, params, this._setLoaded.createDelegate(this));
39844         }
39845     },
39846     
39847     _setLoaded : function(){
39848         this.loaded = true;
39849     }, 
39850     
39851     /**
39852      * Returns this panel's id
39853      * @return {String} 
39854      */
39855     getId : function(){
39856         return this.el.id;
39857     },
39858     
39859     /** 
39860      * Returns this panel's element - used by regiosn to add.
39861      * @return {Roo.Element} 
39862      */
39863     getEl : function(){
39864         return this.wrapEl || this.el;
39865     },
39866     
39867    
39868     
39869     adjustForComponents : function(width, height)
39870     {
39871         //Roo.log('adjustForComponents ');
39872         if(this.resizeEl != this.el){
39873             width -= this.el.getFrameWidth('lr');
39874             height -= this.el.getFrameWidth('tb');
39875         }
39876         if(this.toolbar){
39877             var te = this.toolbar.getEl();
39878             te.setWidth(width);
39879             height -= te.getHeight();
39880         }
39881         if(this.footer){
39882             var te = this.footer.getEl();
39883             te.setWidth(width);
39884             height -= te.getHeight();
39885         }
39886         
39887         
39888         if(this.adjustments){
39889             width += this.adjustments[0];
39890             height += this.adjustments[1];
39891         }
39892         return {"width": width, "height": height};
39893     },
39894     
39895     setSize : function(width, height){
39896         if(this.fitToFrame && !this.ignoreResize(width, height)){
39897             if(this.fitContainer && this.resizeEl != this.el){
39898                 this.el.setSize(width, height);
39899             }
39900             var size = this.adjustForComponents(width, height);
39901             if (this.iframe) {
39902                 this.iframeEl.setSize(width,height);
39903             }
39904             
39905             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39906             this.fireEvent('resize', this, size.width, size.height);
39907             
39908             
39909         }
39910     },
39911     
39912     /**
39913      * Returns this panel's title
39914      * @return {String} 
39915      */
39916     getTitle : function(){
39917         
39918         if (typeof(this.title) != 'object') {
39919             return this.title;
39920         }
39921         
39922         var t = '';
39923         for (var k in this.title) {
39924             if (!this.title.hasOwnProperty(k)) {
39925                 continue;
39926             }
39927             
39928             if (k.indexOf('-') >= 0) {
39929                 var s = k.split('-');
39930                 for (var i = 0; i<s.length; i++) {
39931                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39932                 }
39933             } else {
39934                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39935             }
39936         }
39937         return t;
39938     },
39939     
39940     /**
39941      * Set this panel's title
39942      * @param {String} title
39943      */
39944     setTitle : function(title){
39945         this.title = title;
39946         if(this.region){
39947             this.region.updatePanelTitle(this, title);
39948         }
39949     },
39950     
39951     /**
39952      * Returns true is this panel was configured to be closable
39953      * @return {Boolean} 
39954      */
39955     isClosable : function(){
39956         return this.closable;
39957     },
39958     
39959     beforeSlide : function(){
39960         this.el.clip();
39961         this.resizeEl.clip();
39962     },
39963     
39964     afterSlide : function(){
39965         this.el.unclip();
39966         this.resizeEl.unclip();
39967     },
39968     
39969     /**
39970      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39971      *   Will fail silently if the {@link #setUrl} method has not been called.
39972      *   This does not activate the panel, just updates its content.
39973      */
39974     refresh : function(){
39975         if(this.refreshDelegate){
39976            this.loaded = false;
39977            this.refreshDelegate();
39978         }
39979     },
39980     
39981     /**
39982      * Destroys this panel
39983      */
39984     destroy : function(){
39985         this.el.removeAllListeners();
39986         var tempEl = document.createElement("span");
39987         tempEl.appendChild(this.el.dom);
39988         tempEl.innerHTML = "";
39989         this.el.remove();
39990         this.el = null;
39991     },
39992     
39993     /**
39994      * form - if the content panel contains a form - this is a reference to it.
39995      * @type {Roo.form.Form}
39996      */
39997     form : false,
39998     /**
39999      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40000      *    This contains a reference to it.
40001      * @type {Roo.View}
40002      */
40003     view : false,
40004     
40005       /**
40006      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40007      * <pre><code>
40008
40009 layout.addxtype({
40010        xtype : 'Form',
40011        items: [ .... ]
40012    }
40013 );
40014
40015 </code></pre>
40016      * @param {Object} cfg Xtype definition of item to add.
40017      */
40018     
40019     
40020     getChildContainer: function () {
40021         return this.getEl();
40022     }
40023     
40024     
40025     /*
40026         var  ret = new Roo.factory(cfg);
40027         return ret;
40028         
40029         
40030         // add form..
40031         if (cfg.xtype.match(/^Form$/)) {
40032             
40033             var el;
40034             //if (this.footer) {
40035             //    el = this.footer.container.insertSibling(false, 'before');
40036             //} else {
40037                 el = this.el.createChild();
40038             //}
40039
40040             this.form = new  Roo.form.Form(cfg);
40041             
40042             
40043             if ( this.form.allItems.length) {
40044                 this.form.render(el.dom);
40045             }
40046             return this.form;
40047         }
40048         // should only have one of theses..
40049         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40050             // views.. should not be just added - used named prop 'view''
40051             
40052             cfg.el = this.el.appendChild(document.createElement("div"));
40053             // factory?
40054             
40055             var ret = new Roo.factory(cfg);
40056              
40057              ret.render && ret.render(false, ''); // render blank..
40058             this.view = ret;
40059             return ret;
40060         }
40061         return false;
40062     }
40063     \*/
40064 });
40065  
40066 /**
40067  * @class Roo.bootstrap.panel.Grid
40068  * @extends Roo.bootstrap.panel.Content
40069  * @constructor
40070  * Create a new GridPanel.
40071  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40072  * @param {Object} config A the config object
40073   
40074  */
40075
40076
40077
40078 Roo.bootstrap.panel.Grid = function(config)
40079 {
40080     
40081       
40082     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40083         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40084
40085     config.el = this.wrapper;
40086     //this.el = this.wrapper;
40087     
40088       if (config.container) {
40089         // ctor'ed from a Border/panel.grid
40090         
40091         
40092         this.wrapper.setStyle("overflow", "hidden");
40093         this.wrapper.addClass('roo-grid-container');
40094
40095     }
40096     
40097     
40098     if(config.toolbar){
40099         var tool_el = this.wrapper.createChild();    
40100         this.toolbar = Roo.factory(config.toolbar);
40101         var ti = [];
40102         if (config.toolbar.items) {
40103             ti = config.toolbar.items ;
40104             delete config.toolbar.items ;
40105         }
40106         
40107         var nitems = [];
40108         this.toolbar.render(tool_el);
40109         for(var i =0;i < ti.length;i++) {
40110           //  Roo.log(['add child', items[i]]);
40111             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40112         }
40113         this.toolbar.items = nitems;
40114         
40115         delete config.toolbar;
40116     }
40117     
40118     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40119     config.grid.scrollBody = true;;
40120     config.grid.monitorWindowResize = false; // turn off autosizing
40121     config.grid.autoHeight = false;
40122     config.grid.autoWidth = false;
40123     
40124     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40125     
40126     if (config.background) {
40127         // render grid on panel activation (if panel background)
40128         this.on('activate', function(gp) {
40129             if (!gp.grid.rendered) {
40130                 gp.grid.render(this.wrapper);
40131                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40132             }
40133         });
40134             
40135     } else {
40136         this.grid.render(this.wrapper);
40137         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40138
40139     }
40140     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40141     // ??? needed ??? config.el = this.wrapper;
40142     
40143     
40144     
40145   
40146     // xtype created footer. - not sure if will work as we normally have to render first..
40147     if (this.footer && !this.footer.el && this.footer.xtype) {
40148         
40149         var ctr = this.grid.getView().getFooterPanel(true);
40150         this.footer.dataSource = this.grid.dataSource;
40151         this.footer = Roo.factory(this.footer, Roo);
40152         this.footer.render(ctr);
40153         
40154     }
40155     
40156     
40157     
40158     
40159      
40160 };
40161
40162 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40163     getId : function(){
40164         return this.grid.id;
40165     },
40166     
40167     /**
40168      * Returns the grid for this panel
40169      * @return {Roo.bootstrap.Table} 
40170      */
40171     getGrid : function(){
40172         return this.grid;    
40173     },
40174     
40175     setSize : function(width, height){
40176         if(!this.ignoreResize(width, height)){
40177             var grid = this.grid;
40178             var size = this.adjustForComponents(width, height);
40179             // tfoot is not a footer?
40180           
40181             
40182             var gridel = grid.getGridEl();
40183             gridel.setSize(size.width, size.height);
40184             
40185             var tbd = grid.getGridEl().select('tbody', true).first();
40186             var thd = grid.getGridEl().select('thead',true).first();
40187             var tbf= grid.getGridEl().select('tfoot', true).first();
40188
40189             if (tbf) {
40190                 size.height -= tbf.getHeight();
40191             }
40192             if (thd) {
40193                 size.height -= thd.getHeight();
40194             }
40195             
40196             tbd.setSize(size.width, size.height );
40197             // this is for the account management tab -seems to work there.
40198             var thd = grid.getGridEl().select('thead',true).first();
40199             //if (tbd) {
40200             //    tbd.setSize(size.width, size.height - thd.getHeight());
40201             //}
40202              
40203             grid.autoSize();
40204         }
40205     },
40206      
40207     
40208     
40209     beforeSlide : function(){
40210         this.grid.getView().scroller.clip();
40211     },
40212     
40213     afterSlide : function(){
40214         this.grid.getView().scroller.unclip();
40215     },
40216     
40217     destroy : function(){
40218         this.grid.destroy();
40219         delete this.grid;
40220         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40221     }
40222 });
40223
40224 /**
40225  * @class Roo.bootstrap.panel.Nest
40226  * @extends Roo.bootstrap.panel.Content
40227  * @constructor
40228  * Create a new Panel, that can contain a layout.Border.
40229  * 
40230  * 
40231  * @param {Roo.BorderLayout} layout The layout for this panel
40232  * @param {String/Object} config A string to set only the title or a config object
40233  */
40234 Roo.bootstrap.panel.Nest = function(config)
40235 {
40236     // construct with only one argument..
40237     /* FIXME - implement nicer consturctors
40238     if (layout.layout) {
40239         config = layout;
40240         layout = config.layout;
40241         delete config.layout;
40242     }
40243     if (layout.xtype && !layout.getEl) {
40244         // then layout needs constructing..
40245         layout = Roo.factory(layout, Roo);
40246     }
40247     */
40248     
40249     config.el =  config.layout.getEl();
40250     
40251     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40252     
40253     config.layout.monitorWindowResize = false; // turn off autosizing
40254     this.layout = config.layout;
40255     this.layout.getEl().addClass("roo-layout-nested-layout");
40256     this.layout.parent = this;
40257     
40258     
40259     
40260     
40261 };
40262
40263 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40264
40265     setSize : function(width, height){
40266         if(!this.ignoreResize(width, height)){
40267             var size = this.adjustForComponents(width, height);
40268             var el = this.layout.getEl();
40269             if (size.height < 1) {
40270                 el.setWidth(size.width);   
40271             } else {
40272                 el.setSize(size.width, size.height);
40273             }
40274             var touch = el.dom.offsetWidth;
40275             this.layout.layout();
40276             // ie requires a double layout on the first pass
40277             if(Roo.isIE && !this.initialized){
40278                 this.initialized = true;
40279                 this.layout.layout();
40280             }
40281         }
40282     },
40283     
40284     // activate all subpanels if not currently active..
40285     
40286     setActiveState : function(active){
40287         this.active = active;
40288         this.setActiveClass(active);
40289         
40290         if(!active){
40291             this.fireEvent("deactivate", this);
40292             return;
40293         }
40294         
40295         this.fireEvent("activate", this);
40296         // not sure if this should happen before or after..
40297         if (!this.layout) {
40298             return; // should not happen..
40299         }
40300         var reg = false;
40301         for (var r in this.layout.regions) {
40302             reg = this.layout.getRegion(r);
40303             if (reg.getActivePanel()) {
40304                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40305                 reg.setActivePanel(reg.getActivePanel());
40306                 continue;
40307             }
40308             if (!reg.panels.length) {
40309                 continue;
40310             }
40311             reg.showPanel(reg.getPanel(0));
40312         }
40313         
40314         
40315         
40316         
40317     },
40318     
40319     /**
40320      * Returns the nested BorderLayout for this panel
40321      * @return {Roo.BorderLayout} 
40322      */
40323     getLayout : function(){
40324         return this.layout;
40325     },
40326     
40327      /**
40328      * Adds a xtype elements to the layout of the nested panel
40329      * <pre><code>
40330
40331 panel.addxtype({
40332        xtype : 'ContentPanel',
40333        region: 'west',
40334        items: [ .... ]
40335    }
40336 );
40337
40338 panel.addxtype({
40339         xtype : 'NestedLayoutPanel',
40340         region: 'west',
40341         layout: {
40342            center: { },
40343            west: { }   
40344         },
40345         items : [ ... list of content panels or nested layout panels.. ]
40346    }
40347 );
40348 </code></pre>
40349      * @param {Object} cfg Xtype definition of item to add.
40350      */
40351     addxtype : function(cfg) {
40352         return this.layout.addxtype(cfg);
40353     
40354     }
40355 });/*
40356  * Based on:
40357  * Ext JS Library 1.1.1
40358  * Copyright(c) 2006-2007, Ext JS, LLC.
40359  *
40360  * Originally Released Under LGPL - original licence link has changed is not relivant.
40361  *
40362  * Fork - LGPL
40363  * <script type="text/javascript">
40364  */
40365 /**
40366  * @class Roo.TabPanel
40367  * @extends Roo.util.Observable
40368  * A lightweight tab container.
40369  * <br><br>
40370  * Usage:
40371  * <pre><code>
40372 // basic tabs 1, built from existing content
40373 var tabs = new Roo.TabPanel("tabs1");
40374 tabs.addTab("script", "View Script");
40375 tabs.addTab("markup", "View Markup");
40376 tabs.activate("script");
40377
40378 // more advanced tabs, built from javascript
40379 var jtabs = new Roo.TabPanel("jtabs");
40380 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40381
40382 // set up the UpdateManager
40383 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40384 var updater = tab2.getUpdateManager();
40385 updater.setDefaultUrl("ajax1.htm");
40386 tab2.on('activate', updater.refresh, updater, true);
40387
40388 // Use setUrl for Ajax loading
40389 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40390 tab3.setUrl("ajax2.htm", null, true);
40391
40392 // Disabled tab
40393 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40394 tab4.disable();
40395
40396 jtabs.activate("jtabs-1");
40397  * </code></pre>
40398  * @constructor
40399  * Create a new TabPanel.
40400  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40401  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40402  */
40403 Roo.bootstrap.panel.Tabs = function(config){
40404     /**
40405     * The container element for this TabPanel.
40406     * @type Roo.Element
40407     */
40408     this.el = Roo.get(config.el);
40409     delete config.el;
40410     if(config){
40411         if(typeof config == "boolean"){
40412             this.tabPosition = config ? "bottom" : "top";
40413         }else{
40414             Roo.apply(this, config);
40415         }
40416     }
40417     
40418     if(this.tabPosition == "bottom"){
40419         // if tabs are at the bottom = create the body first.
40420         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40421         this.el.addClass("roo-tabs-bottom");
40422     }
40423     // next create the tabs holders
40424     
40425     if (this.tabPosition == "west"){
40426         
40427         var reg = this.region; // fake it..
40428         while (reg) {
40429             if (!reg.mgr.parent) {
40430                 break;
40431             }
40432             reg = reg.mgr.parent.region;
40433         }
40434         Roo.log("got nest?");
40435         Roo.log(reg);
40436         if (reg.mgr.getRegion('west')) {
40437             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40438             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40439             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40440             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40441             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40442         
40443             
40444         }
40445         
40446         
40447     } else {
40448      
40449         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40450         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40451         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40452         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40453     }
40454     
40455     
40456     if(Roo.isIE){
40457         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40458     }
40459     
40460     // finally - if tabs are at the top, then create the body last..
40461     if(this.tabPosition != "bottom"){
40462         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40463          * @type Roo.Element
40464          */
40465         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40466         this.el.addClass("roo-tabs-top");
40467     }
40468     this.items = [];
40469
40470     this.bodyEl.setStyle("position", "relative");
40471
40472     this.active = null;
40473     this.activateDelegate = this.activate.createDelegate(this);
40474
40475     this.addEvents({
40476         /**
40477          * @event tabchange
40478          * Fires when the active tab changes
40479          * @param {Roo.TabPanel} this
40480          * @param {Roo.TabPanelItem} activePanel The new active tab
40481          */
40482         "tabchange": true,
40483         /**
40484          * @event beforetabchange
40485          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40486          * @param {Roo.TabPanel} this
40487          * @param {Object} e Set cancel to true on this object to cancel the tab change
40488          * @param {Roo.TabPanelItem} tab The tab being changed to
40489          */
40490         "beforetabchange" : true
40491     });
40492
40493     Roo.EventManager.onWindowResize(this.onResize, this);
40494     this.cpad = this.el.getPadding("lr");
40495     this.hiddenCount = 0;
40496
40497
40498     // toolbar on the tabbar support...
40499     if (this.toolbar) {
40500         alert("no toolbar support yet");
40501         this.toolbar  = false;
40502         /*
40503         var tcfg = this.toolbar;
40504         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40505         this.toolbar = new Roo.Toolbar(tcfg);
40506         if (Roo.isSafari) {
40507             var tbl = tcfg.container.child('table', true);
40508             tbl.setAttribute('width', '100%');
40509         }
40510         */
40511         
40512     }
40513    
40514
40515
40516     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40517 };
40518
40519 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40520     /*
40521      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40522      */
40523     tabPosition : "top",
40524     /*
40525      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40526      */
40527     currentTabWidth : 0,
40528     /*
40529      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40530      */
40531     minTabWidth : 40,
40532     /*
40533      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40534      */
40535     maxTabWidth : 250,
40536     /*
40537      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40538      */
40539     preferredTabWidth : 175,
40540     /*
40541      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40542      */
40543     resizeTabs : false,
40544     /*
40545      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40546      */
40547     monitorResize : true,
40548     /*
40549      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40550      */
40551     toolbar : false,  // set by caller..
40552     
40553     region : false, /// set by caller
40554     
40555     disableTooltips : true, // not used yet...
40556
40557     /**
40558      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40559      * @param {String} id The id of the div to use <b>or create</b>
40560      * @param {String} text The text for the tab
40561      * @param {String} content (optional) Content to put in the TabPanelItem body
40562      * @param {Boolean} closable (optional) True to create a close icon on the tab
40563      * @return {Roo.TabPanelItem} The created TabPanelItem
40564      */
40565     addTab : function(id, text, content, closable, tpl)
40566     {
40567         var item = new Roo.bootstrap.panel.TabItem({
40568             panel: this,
40569             id : id,
40570             text : text,
40571             closable : closable,
40572             tpl : tpl
40573         });
40574         this.addTabItem(item);
40575         if(content){
40576             item.setContent(content);
40577         }
40578         return item;
40579     },
40580
40581     /**
40582      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40583      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40584      * @return {Roo.TabPanelItem}
40585      */
40586     getTab : function(id){
40587         return this.items[id];
40588     },
40589
40590     /**
40591      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40592      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40593      */
40594     hideTab : function(id){
40595         var t = this.items[id];
40596         if(!t.isHidden()){
40597            t.setHidden(true);
40598            this.hiddenCount++;
40599            this.autoSizeTabs();
40600         }
40601     },
40602
40603     /**
40604      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40605      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40606      */
40607     unhideTab : function(id){
40608         var t = this.items[id];
40609         if(t.isHidden()){
40610            t.setHidden(false);
40611            this.hiddenCount--;
40612            this.autoSizeTabs();
40613         }
40614     },
40615
40616     /**
40617      * Adds an existing {@link Roo.TabPanelItem}.
40618      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40619      */
40620     addTabItem : function(item)
40621     {
40622         this.items[item.id] = item;
40623         this.items.push(item);
40624         this.autoSizeTabs();
40625       //  if(this.resizeTabs){
40626     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40627   //         this.autoSizeTabs();
40628 //        }else{
40629 //            item.autoSize();
40630        // }
40631     },
40632
40633     /**
40634      * Removes a {@link Roo.TabPanelItem}.
40635      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40636      */
40637     removeTab : function(id){
40638         var items = this.items;
40639         var tab = items[id];
40640         if(!tab) { return; }
40641         var index = items.indexOf(tab);
40642         if(this.active == tab && items.length > 1){
40643             var newTab = this.getNextAvailable(index);
40644             if(newTab) {
40645                 newTab.activate();
40646             }
40647         }
40648         this.stripEl.dom.removeChild(tab.pnode.dom);
40649         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40650             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40651         }
40652         items.splice(index, 1);
40653         delete this.items[tab.id];
40654         tab.fireEvent("close", tab);
40655         tab.purgeListeners();
40656         this.autoSizeTabs();
40657     },
40658
40659     getNextAvailable : function(start){
40660         var items = this.items;
40661         var index = start;
40662         // look for a next tab that will slide over to
40663         // replace the one being removed
40664         while(index < items.length){
40665             var item = items[++index];
40666             if(item && !item.isHidden()){
40667                 return item;
40668             }
40669         }
40670         // if one isn't found select the previous tab (on the left)
40671         index = start;
40672         while(index >= 0){
40673             var item = items[--index];
40674             if(item && !item.isHidden()){
40675                 return item;
40676             }
40677         }
40678         return null;
40679     },
40680
40681     /**
40682      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40683      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40684      */
40685     disableTab : function(id){
40686         var tab = this.items[id];
40687         if(tab && this.active != tab){
40688             tab.disable();
40689         }
40690     },
40691
40692     /**
40693      * Enables a {@link Roo.TabPanelItem} that is disabled.
40694      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40695      */
40696     enableTab : function(id){
40697         var tab = this.items[id];
40698         tab.enable();
40699     },
40700
40701     /**
40702      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40703      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40704      * @return {Roo.TabPanelItem} The TabPanelItem.
40705      */
40706     activate : function(id)
40707     {
40708         //Roo.log('activite:'  + id);
40709         
40710         var tab = this.items[id];
40711         if(!tab){
40712             return null;
40713         }
40714         if(tab == this.active || tab.disabled){
40715             return tab;
40716         }
40717         var e = {};
40718         this.fireEvent("beforetabchange", this, e, tab);
40719         if(e.cancel !== true && !tab.disabled){
40720             if(this.active){
40721                 this.active.hide();
40722             }
40723             this.active = this.items[id];
40724             this.active.show();
40725             this.fireEvent("tabchange", this, this.active);
40726         }
40727         return tab;
40728     },
40729
40730     /**
40731      * Gets the active {@link Roo.TabPanelItem}.
40732      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40733      */
40734     getActiveTab : function(){
40735         return this.active;
40736     },
40737
40738     /**
40739      * Updates the tab body element to fit the height of the container element
40740      * for overflow scrolling
40741      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40742      */
40743     syncHeight : function(targetHeight){
40744         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40745         var bm = this.bodyEl.getMargins();
40746         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40747         this.bodyEl.setHeight(newHeight);
40748         return newHeight;
40749     },
40750
40751     onResize : function(){
40752         if(this.monitorResize){
40753             this.autoSizeTabs();
40754         }
40755     },
40756
40757     /**
40758      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40759      */
40760     beginUpdate : function(){
40761         this.updating = true;
40762     },
40763
40764     /**
40765      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40766      */
40767     endUpdate : function(){
40768         this.updating = false;
40769         this.autoSizeTabs();
40770     },
40771
40772     /**
40773      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40774      */
40775     autoSizeTabs : function()
40776     {
40777         var count = this.items.length;
40778         var vcount = count - this.hiddenCount;
40779         
40780         if (vcount < 2) {
40781             this.stripEl.hide();
40782         } else {
40783             this.stripEl.show();
40784         }
40785         
40786         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40787             return;
40788         }
40789         
40790         
40791         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40792         var availWidth = Math.floor(w / vcount);
40793         var b = this.stripBody;
40794         if(b.getWidth() > w){
40795             var tabs = this.items;
40796             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40797             if(availWidth < this.minTabWidth){
40798                 /*if(!this.sleft){    // incomplete scrolling code
40799                     this.createScrollButtons();
40800                 }
40801                 this.showScroll();
40802                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40803             }
40804         }else{
40805             if(this.currentTabWidth < this.preferredTabWidth){
40806                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40807             }
40808         }
40809     },
40810
40811     /**
40812      * Returns the number of tabs in this TabPanel.
40813      * @return {Number}
40814      */
40815      getCount : function(){
40816          return this.items.length;
40817      },
40818
40819     /**
40820      * Resizes all the tabs to the passed width
40821      * @param {Number} The new width
40822      */
40823     setTabWidth : function(width){
40824         this.currentTabWidth = width;
40825         for(var i = 0, len = this.items.length; i < len; i++) {
40826                 if(!this.items[i].isHidden()) {
40827                 this.items[i].setWidth(width);
40828             }
40829         }
40830     },
40831
40832     /**
40833      * Destroys this TabPanel
40834      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40835      */
40836     destroy : function(removeEl){
40837         Roo.EventManager.removeResizeListener(this.onResize, this);
40838         for(var i = 0, len = this.items.length; i < len; i++){
40839             this.items[i].purgeListeners();
40840         }
40841         if(removeEl === true){
40842             this.el.update("");
40843             this.el.remove();
40844         }
40845     },
40846     
40847     createStrip : function(container)
40848     {
40849         var strip = document.createElement("nav");
40850         strip.className = Roo.bootstrap.version == 4 ?
40851             "navbar-light bg-light" : 
40852             "navbar navbar-default"; //"x-tabs-wrap";
40853         container.appendChild(strip);
40854         return strip;
40855     },
40856     
40857     createStripList : function(strip)
40858     {
40859         // div wrapper for retard IE
40860         // returns the "tr" element.
40861         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40862         //'<div class="x-tabs-strip-wrap">'+
40863           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40864           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40865         return strip.firstChild; //.firstChild.firstChild.firstChild;
40866     },
40867     createBody : function(container)
40868     {
40869         var body = document.createElement("div");
40870         Roo.id(body, "tab-body");
40871         //Roo.fly(body).addClass("x-tabs-body");
40872         Roo.fly(body).addClass("tab-content");
40873         container.appendChild(body);
40874         return body;
40875     },
40876     createItemBody :function(bodyEl, id){
40877         var body = Roo.getDom(id);
40878         if(!body){
40879             body = document.createElement("div");
40880             body.id = id;
40881         }
40882         //Roo.fly(body).addClass("x-tabs-item-body");
40883         Roo.fly(body).addClass("tab-pane");
40884          bodyEl.insertBefore(body, bodyEl.firstChild);
40885         return body;
40886     },
40887     /** @private */
40888     createStripElements :  function(stripEl, text, closable, tpl)
40889     {
40890         var td = document.createElement("li"); // was td..
40891         td.className = 'nav-item';
40892         
40893         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40894         
40895         
40896         stripEl.appendChild(td);
40897         /*if(closable){
40898             td.className = "x-tabs-closable";
40899             if(!this.closeTpl){
40900                 this.closeTpl = new Roo.Template(
40901                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40902                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40903                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40904                 );
40905             }
40906             var el = this.closeTpl.overwrite(td, {"text": text});
40907             var close = el.getElementsByTagName("div")[0];
40908             var inner = el.getElementsByTagName("em")[0];
40909             return {"el": el, "close": close, "inner": inner};
40910         } else {
40911         */
40912         // not sure what this is..
40913 //            if(!this.tabTpl){
40914                 //this.tabTpl = new Roo.Template(
40915                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40916                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40917                 //);
40918 //                this.tabTpl = new Roo.Template(
40919 //                   '<a href="#">' +
40920 //                   '<span unselectable="on"' +
40921 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40922 //                            ' >{text}</span></a>'
40923 //                );
40924 //                
40925 //            }
40926
40927
40928             var template = tpl || this.tabTpl || false;
40929             
40930             if(!template){
40931                 template =  new Roo.Template(
40932                         Roo.bootstrap.version == 4 ? 
40933                             (
40934                                 '<a class="nav-link" href="#" unselectable="on"' +
40935                                      (this.disableTooltips ? '' : ' title="{text}"') +
40936                                      ' >{text}</a>'
40937                             ) : (
40938                                 '<a class="nav-link" href="#">' +
40939                                 '<span unselectable="on"' +
40940                                          (this.disableTooltips ? '' : ' title="{text}"') +
40941                                     ' >{text}</span></a>'
40942                             )
40943                 );
40944             }
40945             
40946             switch (typeof(template)) {
40947                 case 'object' :
40948                     break;
40949                 case 'string' :
40950                     template = new Roo.Template(template);
40951                     break;
40952                 default :
40953                     break;
40954             }
40955             
40956             var el = template.overwrite(td, {"text": text});
40957             
40958             var inner = el.getElementsByTagName("span")[0];
40959             
40960             return {"el": el, "inner": inner};
40961             
40962     }
40963         
40964     
40965 });
40966
40967 /**
40968  * @class Roo.TabPanelItem
40969  * @extends Roo.util.Observable
40970  * Represents an individual item (tab plus body) in a TabPanel.
40971  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40972  * @param {String} id The id of this TabPanelItem
40973  * @param {String} text The text for the tab of this TabPanelItem
40974  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40975  */
40976 Roo.bootstrap.panel.TabItem = function(config){
40977     /**
40978      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40979      * @type Roo.TabPanel
40980      */
40981     this.tabPanel = config.panel;
40982     /**
40983      * The id for this TabPanelItem
40984      * @type String
40985      */
40986     this.id = config.id;
40987     /** @private */
40988     this.disabled = false;
40989     /** @private */
40990     this.text = config.text;
40991     /** @private */
40992     this.loaded = false;
40993     this.closable = config.closable;
40994
40995     /**
40996      * The body element for this TabPanelItem.
40997      * @type Roo.Element
40998      */
40999     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41000     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41001     this.bodyEl.setStyle("display", "block");
41002     this.bodyEl.setStyle("zoom", "1");
41003     //this.hideAction();
41004
41005     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41006     /** @private */
41007     this.el = Roo.get(els.el);
41008     this.inner = Roo.get(els.inner, true);
41009      this.textEl = Roo.bootstrap.version == 4 ?
41010         this.el : Roo.get(this.el.dom.firstChild, true);
41011
41012     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41013     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41014
41015     
41016 //    this.el.on("mousedown", this.onTabMouseDown, this);
41017     this.el.on("click", this.onTabClick, this);
41018     /** @private */
41019     if(config.closable){
41020         var c = Roo.get(els.close, true);
41021         c.dom.title = this.closeText;
41022         c.addClassOnOver("close-over");
41023         c.on("click", this.closeClick, this);
41024      }
41025
41026     this.addEvents({
41027          /**
41028          * @event activate
41029          * Fires when this tab becomes the active tab.
41030          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41031          * @param {Roo.TabPanelItem} this
41032          */
41033         "activate": true,
41034         /**
41035          * @event beforeclose
41036          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41037          * @param {Roo.TabPanelItem} this
41038          * @param {Object} e Set cancel to true on this object to cancel the close.
41039          */
41040         "beforeclose": true,
41041         /**
41042          * @event close
41043          * Fires when this tab is closed.
41044          * @param {Roo.TabPanelItem} this
41045          */
41046          "close": true,
41047         /**
41048          * @event deactivate
41049          * Fires when this tab is no longer the active tab.
41050          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41051          * @param {Roo.TabPanelItem} this
41052          */
41053          "deactivate" : true
41054     });
41055     this.hidden = false;
41056
41057     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41058 };
41059
41060 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41061            {
41062     purgeListeners : function(){
41063        Roo.util.Observable.prototype.purgeListeners.call(this);
41064        this.el.removeAllListeners();
41065     },
41066     /**
41067      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41068      */
41069     show : function(){
41070         this.status_node.addClass("active");
41071         this.showAction();
41072         if(Roo.isOpera){
41073             this.tabPanel.stripWrap.repaint();
41074         }
41075         this.fireEvent("activate", this.tabPanel, this);
41076     },
41077
41078     /**
41079      * Returns true if this tab is the active tab.
41080      * @return {Boolean}
41081      */
41082     isActive : function(){
41083         return this.tabPanel.getActiveTab() == this;
41084     },
41085
41086     /**
41087      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41088      */
41089     hide : function(){
41090         this.status_node.removeClass("active");
41091         this.hideAction();
41092         this.fireEvent("deactivate", this.tabPanel, this);
41093     },
41094
41095     hideAction : function(){
41096         this.bodyEl.hide();
41097         this.bodyEl.setStyle("position", "absolute");
41098         this.bodyEl.setLeft("-20000px");
41099         this.bodyEl.setTop("-20000px");
41100     },
41101
41102     showAction : function(){
41103         this.bodyEl.setStyle("position", "relative");
41104         this.bodyEl.setTop("");
41105         this.bodyEl.setLeft("");
41106         this.bodyEl.show();
41107     },
41108
41109     /**
41110      * Set the tooltip for the tab.
41111      * @param {String} tooltip The tab's tooltip
41112      */
41113     setTooltip : function(text){
41114         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41115             this.textEl.dom.qtip = text;
41116             this.textEl.dom.removeAttribute('title');
41117         }else{
41118             this.textEl.dom.title = text;
41119         }
41120     },
41121
41122     onTabClick : function(e){
41123         e.preventDefault();
41124         this.tabPanel.activate(this.id);
41125     },
41126
41127     onTabMouseDown : function(e){
41128         e.preventDefault();
41129         this.tabPanel.activate(this.id);
41130     },
41131 /*
41132     getWidth : function(){
41133         return this.inner.getWidth();
41134     },
41135
41136     setWidth : function(width){
41137         var iwidth = width - this.linode.getPadding("lr");
41138         this.inner.setWidth(iwidth);
41139         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41140         this.linode.setWidth(width);
41141     },
41142 */
41143     /**
41144      * Show or hide the tab
41145      * @param {Boolean} hidden True to hide or false to show.
41146      */
41147     setHidden : function(hidden){
41148         this.hidden = hidden;
41149         this.linode.setStyle("display", hidden ? "none" : "");
41150     },
41151
41152     /**
41153      * Returns true if this tab is "hidden"
41154      * @return {Boolean}
41155      */
41156     isHidden : function(){
41157         return this.hidden;
41158     },
41159
41160     /**
41161      * Returns the text for this tab
41162      * @return {String}
41163      */
41164     getText : function(){
41165         return this.text;
41166     },
41167     /*
41168     autoSize : function(){
41169         //this.el.beginMeasure();
41170         this.textEl.setWidth(1);
41171         /*
41172          *  #2804 [new] Tabs in Roojs
41173          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41174          */
41175         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41176         //this.el.endMeasure();
41177     //},
41178
41179     /**
41180      * Sets the text for the tab (Note: this also sets the tooltip text)
41181      * @param {String} text The tab's text and tooltip
41182      */
41183     setText : function(text){
41184         this.text = text;
41185         this.textEl.update(text);
41186         this.setTooltip(text);
41187         //if(!this.tabPanel.resizeTabs){
41188         //    this.autoSize();
41189         //}
41190     },
41191     /**
41192      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41193      */
41194     activate : function(){
41195         this.tabPanel.activate(this.id);
41196     },
41197
41198     /**
41199      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41200      */
41201     disable : function(){
41202         if(this.tabPanel.active != this){
41203             this.disabled = true;
41204             this.status_node.addClass("disabled");
41205         }
41206     },
41207
41208     /**
41209      * Enables this TabPanelItem if it was previously disabled.
41210      */
41211     enable : function(){
41212         this.disabled = false;
41213         this.status_node.removeClass("disabled");
41214     },
41215
41216     /**
41217      * Sets the content for this TabPanelItem.
41218      * @param {String} content The content
41219      * @param {Boolean} loadScripts true to look for and load scripts
41220      */
41221     setContent : function(content, loadScripts){
41222         this.bodyEl.update(content, loadScripts);
41223     },
41224
41225     /**
41226      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41227      * @return {Roo.UpdateManager} The UpdateManager
41228      */
41229     getUpdateManager : function(){
41230         return this.bodyEl.getUpdateManager();
41231     },
41232
41233     /**
41234      * Set a URL to be used to load the content for this TabPanelItem.
41235      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41236      * @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)
41237      * @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)
41238      * @return {Roo.UpdateManager} The UpdateManager
41239      */
41240     setUrl : function(url, params, loadOnce){
41241         if(this.refreshDelegate){
41242             this.un('activate', this.refreshDelegate);
41243         }
41244         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41245         this.on("activate", this.refreshDelegate);
41246         return this.bodyEl.getUpdateManager();
41247     },
41248
41249     /** @private */
41250     _handleRefresh : function(url, params, loadOnce){
41251         if(!loadOnce || !this.loaded){
41252             var updater = this.bodyEl.getUpdateManager();
41253             updater.update(url, params, this._setLoaded.createDelegate(this));
41254         }
41255     },
41256
41257     /**
41258      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41259      *   Will fail silently if the setUrl method has not been called.
41260      *   This does not activate the panel, just updates its content.
41261      */
41262     refresh : function(){
41263         if(this.refreshDelegate){
41264            this.loaded = false;
41265            this.refreshDelegate();
41266         }
41267     },
41268
41269     /** @private */
41270     _setLoaded : function(){
41271         this.loaded = true;
41272     },
41273
41274     /** @private */
41275     closeClick : function(e){
41276         var o = {};
41277         e.stopEvent();
41278         this.fireEvent("beforeclose", this, o);
41279         if(o.cancel !== true){
41280             this.tabPanel.removeTab(this.id);
41281         }
41282     },
41283     /**
41284      * The text displayed in the tooltip for the close icon.
41285      * @type String
41286      */
41287     closeText : "Close this tab"
41288 });
41289 /**
41290 *    This script refer to:
41291 *    Title: International Telephone Input
41292 *    Author: Jack O'Connor
41293 *    Code version:  v12.1.12
41294 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41295 **/
41296
41297 Roo.bootstrap.PhoneInputData = function() {
41298     var d = [
41299       [
41300         "Afghanistan (‫افغانستان‬‎)",
41301         "af",
41302         "93"
41303       ],
41304       [
41305         "Albania (Shqipëri)",
41306         "al",
41307         "355"
41308       ],
41309       [
41310         "Algeria (‫الجزائر‬‎)",
41311         "dz",
41312         "213"
41313       ],
41314       [
41315         "American Samoa",
41316         "as",
41317         "1684"
41318       ],
41319       [
41320         "Andorra",
41321         "ad",
41322         "376"
41323       ],
41324       [
41325         "Angola",
41326         "ao",
41327         "244"
41328       ],
41329       [
41330         "Anguilla",
41331         "ai",
41332         "1264"
41333       ],
41334       [
41335         "Antigua and Barbuda",
41336         "ag",
41337         "1268"
41338       ],
41339       [
41340         "Argentina",
41341         "ar",
41342         "54"
41343       ],
41344       [
41345         "Armenia (Հայաստան)",
41346         "am",
41347         "374"
41348       ],
41349       [
41350         "Aruba",
41351         "aw",
41352         "297"
41353       ],
41354       [
41355         "Australia",
41356         "au",
41357         "61",
41358         0
41359       ],
41360       [
41361         "Austria (Österreich)",
41362         "at",
41363         "43"
41364       ],
41365       [
41366         "Azerbaijan (Azərbaycan)",
41367         "az",
41368         "994"
41369       ],
41370       [
41371         "Bahamas",
41372         "bs",
41373         "1242"
41374       ],
41375       [
41376         "Bahrain (‫البحرين‬‎)",
41377         "bh",
41378         "973"
41379       ],
41380       [
41381         "Bangladesh (বাংলাদেশ)",
41382         "bd",
41383         "880"
41384       ],
41385       [
41386         "Barbados",
41387         "bb",
41388         "1246"
41389       ],
41390       [
41391         "Belarus (Беларусь)",
41392         "by",
41393         "375"
41394       ],
41395       [
41396         "Belgium (België)",
41397         "be",
41398         "32"
41399       ],
41400       [
41401         "Belize",
41402         "bz",
41403         "501"
41404       ],
41405       [
41406         "Benin (Bénin)",
41407         "bj",
41408         "229"
41409       ],
41410       [
41411         "Bermuda",
41412         "bm",
41413         "1441"
41414       ],
41415       [
41416         "Bhutan (འབྲུག)",
41417         "bt",
41418         "975"
41419       ],
41420       [
41421         "Bolivia",
41422         "bo",
41423         "591"
41424       ],
41425       [
41426         "Bosnia and Herzegovina (Босна и Херцеговина)",
41427         "ba",
41428         "387"
41429       ],
41430       [
41431         "Botswana",
41432         "bw",
41433         "267"
41434       ],
41435       [
41436         "Brazil (Brasil)",
41437         "br",
41438         "55"
41439       ],
41440       [
41441         "British Indian Ocean Territory",
41442         "io",
41443         "246"
41444       ],
41445       [
41446         "British Virgin Islands",
41447         "vg",
41448         "1284"
41449       ],
41450       [
41451         "Brunei",
41452         "bn",
41453         "673"
41454       ],
41455       [
41456         "Bulgaria (България)",
41457         "bg",
41458         "359"
41459       ],
41460       [
41461         "Burkina Faso",
41462         "bf",
41463         "226"
41464       ],
41465       [
41466         "Burundi (Uburundi)",
41467         "bi",
41468         "257"
41469       ],
41470       [
41471         "Cambodia (កម្ពុជា)",
41472         "kh",
41473         "855"
41474       ],
41475       [
41476         "Cameroon (Cameroun)",
41477         "cm",
41478         "237"
41479       ],
41480       [
41481         "Canada",
41482         "ca",
41483         "1",
41484         1,
41485         ["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"]
41486       ],
41487       [
41488         "Cape Verde (Kabu Verdi)",
41489         "cv",
41490         "238"
41491       ],
41492       [
41493         "Caribbean Netherlands",
41494         "bq",
41495         "599",
41496         1
41497       ],
41498       [
41499         "Cayman Islands",
41500         "ky",
41501         "1345"
41502       ],
41503       [
41504         "Central African Republic (République centrafricaine)",
41505         "cf",
41506         "236"
41507       ],
41508       [
41509         "Chad (Tchad)",
41510         "td",
41511         "235"
41512       ],
41513       [
41514         "Chile",
41515         "cl",
41516         "56"
41517       ],
41518       [
41519         "China (中国)",
41520         "cn",
41521         "86"
41522       ],
41523       [
41524         "Christmas Island",
41525         "cx",
41526         "61",
41527         2
41528       ],
41529       [
41530         "Cocos (Keeling) Islands",
41531         "cc",
41532         "61",
41533         1
41534       ],
41535       [
41536         "Colombia",
41537         "co",
41538         "57"
41539       ],
41540       [
41541         "Comoros (‫جزر القمر‬‎)",
41542         "km",
41543         "269"
41544       ],
41545       [
41546         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41547         "cd",
41548         "243"
41549       ],
41550       [
41551         "Congo (Republic) (Congo-Brazzaville)",
41552         "cg",
41553         "242"
41554       ],
41555       [
41556         "Cook Islands",
41557         "ck",
41558         "682"
41559       ],
41560       [
41561         "Costa Rica",
41562         "cr",
41563         "506"
41564       ],
41565       [
41566         "Côte d’Ivoire",
41567         "ci",
41568         "225"
41569       ],
41570       [
41571         "Croatia (Hrvatska)",
41572         "hr",
41573         "385"
41574       ],
41575       [
41576         "Cuba",
41577         "cu",
41578         "53"
41579       ],
41580       [
41581         "Curaçao",
41582         "cw",
41583         "599",
41584         0
41585       ],
41586       [
41587         "Cyprus (Κύπρος)",
41588         "cy",
41589         "357"
41590       ],
41591       [
41592         "Czech Republic (Česká republika)",
41593         "cz",
41594         "420"
41595       ],
41596       [
41597         "Denmark (Danmark)",
41598         "dk",
41599         "45"
41600       ],
41601       [
41602         "Djibouti",
41603         "dj",
41604         "253"
41605       ],
41606       [
41607         "Dominica",
41608         "dm",
41609         "1767"
41610       ],
41611       [
41612         "Dominican Republic (República Dominicana)",
41613         "do",
41614         "1",
41615         2,
41616         ["809", "829", "849"]
41617       ],
41618       [
41619         "Ecuador",
41620         "ec",
41621         "593"
41622       ],
41623       [
41624         "Egypt (‫مصر‬‎)",
41625         "eg",
41626         "20"
41627       ],
41628       [
41629         "El Salvador",
41630         "sv",
41631         "503"
41632       ],
41633       [
41634         "Equatorial Guinea (Guinea Ecuatorial)",
41635         "gq",
41636         "240"
41637       ],
41638       [
41639         "Eritrea",
41640         "er",
41641         "291"
41642       ],
41643       [
41644         "Estonia (Eesti)",
41645         "ee",
41646         "372"
41647       ],
41648       [
41649         "Ethiopia",
41650         "et",
41651         "251"
41652       ],
41653       [
41654         "Falkland Islands (Islas Malvinas)",
41655         "fk",
41656         "500"
41657       ],
41658       [
41659         "Faroe Islands (Føroyar)",
41660         "fo",
41661         "298"
41662       ],
41663       [
41664         "Fiji",
41665         "fj",
41666         "679"
41667       ],
41668       [
41669         "Finland (Suomi)",
41670         "fi",
41671         "358",
41672         0
41673       ],
41674       [
41675         "France",
41676         "fr",
41677         "33"
41678       ],
41679       [
41680         "French Guiana (Guyane française)",
41681         "gf",
41682         "594"
41683       ],
41684       [
41685         "French Polynesia (Polynésie française)",
41686         "pf",
41687         "689"
41688       ],
41689       [
41690         "Gabon",
41691         "ga",
41692         "241"
41693       ],
41694       [
41695         "Gambia",
41696         "gm",
41697         "220"
41698       ],
41699       [
41700         "Georgia (საქართველო)",
41701         "ge",
41702         "995"
41703       ],
41704       [
41705         "Germany (Deutschland)",
41706         "de",
41707         "49"
41708       ],
41709       [
41710         "Ghana (Gaana)",
41711         "gh",
41712         "233"
41713       ],
41714       [
41715         "Gibraltar",
41716         "gi",
41717         "350"
41718       ],
41719       [
41720         "Greece (Ελλάδα)",
41721         "gr",
41722         "30"
41723       ],
41724       [
41725         "Greenland (Kalaallit Nunaat)",
41726         "gl",
41727         "299"
41728       ],
41729       [
41730         "Grenada",
41731         "gd",
41732         "1473"
41733       ],
41734       [
41735         "Guadeloupe",
41736         "gp",
41737         "590",
41738         0
41739       ],
41740       [
41741         "Guam",
41742         "gu",
41743         "1671"
41744       ],
41745       [
41746         "Guatemala",
41747         "gt",
41748         "502"
41749       ],
41750       [
41751         "Guernsey",
41752         "gg",
41753         "44",
41754         1
41755       ],
41756       [
41757         "Guinea (Guinée)",
41758         "gn",
41759         "224"
41760       ],
41761       [
41762         "Guinea-Bissau (Guiné Bissau)",
41763         "gw",
41764         "245"
41765       ],
41766       [
41767         "Guyana",
41768         "gy",
41769         "592"
41770       ],
41771       [
41772         "Haiti",
41773         "ht",
41774         "509"
41775       ],
41776       [
41777         "Honduras",
41778         "hn",
41779         "504"
41780       ],
41781       [
41782         "Hong Kong (香港)",
41783         "hk",
41784         "852"
41785       ],
41786       [
41787         "Hungary (Magyarország)",
41788         "hu",
41789         "36"
41790       ],
41791       [
41792         "Iceland (Ísland)",
41793         "is",
41794         "354"
41795       ],
41796       [
41797         "India (भारत)",
41798         "in",
41799         "91"
41800       ],
41801       [
41802         "Indonesia",
41803         "id",
41804         "62"
41805       ],
41806       [
41807         "Iran (‫ایران‬‎)",
41808         "ir",
41809         "98"
41810       ],
41811       [
41812         "Iraq (‫العراق‬‎)",
41813         "iq",
41814         "964"
41815       ],
41816       [
41817         "Ireland",
41818         "ie",
41819         "353"
41820       ],
41821       [
41822         "Isle of Man",
41823         "im",
41824         "44",
41825         2
41826       ],
41827       [
41828         "Israel (‫ישראל‬‎)",
41829         "il",
41830         "972"
41831       ],
41832       [
41833         "Italy (Italia)",
41834         "it",
41835         "39",
41836         0
41837       ],
41838       [
41839         "Jamaica",
41840         "jm",
41841         "1876"
41842       ],
41843       [
41844         "Japan (日本)",
41845         "jp",
41846         "81"
41847       ],
41848       [
41849         "Jersey",
41850         "je",
41851         "44",
41852         3
41853       ],
41854       [
41855         "Jordan (‫الأردن‬‎)",
41856         "jo",
41857         "962"
41858       ],
41859       [
41860         "Kazakhstan (Казахстан)",
41861         "kz",
41862         "7",
41863         1
41864       ],
41865       [
41866         "Kenya",
41867         "ke",
41868         "254"
41869       ],
41870       [
41871         "Kiribati",
41872         "ki",
41873         "686"
41874       ],
41875       [
41876         "Kosovo",
41877         "xk",
41878         "383"
41879       ],
41880       [
41881         "Kuwait (‫الكويت‬‎)",
41882         "kw",
41883         "965"
41884       ],
41885       [
41886         "Kyrgyzstan (Кыргызстан)",
41887         "kg",
41888         "996"
41889       ],
41890       [
41891         "Laos (ລາວ)",
41892         "la",
41893         "856"
41894       ],
41895       [
41896         "Latvia (Latvija)",
41897         "lv",
41898         "371"
41899       ],
41900       [
41901         "Lebanon (‫لبنان‬‎)",
41902         "lb",
41903         "961"
41904       ],
41905       [
41906         "Lesotho",
41907         "ls",
41908         "266"
41909       ],
41910       [
41911         "Liberia",
41912         "lr",
41913         "231"
41914       ],
41915       [
41916         "Libya (‫ليبيا‬‎)",
41917         "ly",
41918         "218"
41919       ],
41920       [
41921         "Liechtenstein",
41922         "li",
41923         "423"
41924       ],
41925       [
41926         "Lithuania (Lietuva)",
41927         "lt",
41928         "370"
41929       ],
41930       [
41931         "Luxembourg",
41932         "lu",
41933         "352"
41934       ],
41935       [
41936         "Macau (澳門)",
41937         "mo",
41938         "853"
41939       ],
41940       [
41941         "Macedonia (FYROM) (Македонија)",
41942         "mk",
41943         "389"
41944       ],
41945       [
41946         "Madagascar (Madagasikara)",
41947         "mg",
41948         "261"
41949       ],
41950       [
41951         "Malawi",
41952         "mw",
41953         "265"
41954       ],
41955       [
41956         "Malaysia",
41957         "my",
41958         "60"
41959       ],
41960       [
41961         "Maldives",
41962         "mv",
41963         "960"
41964       ],
41965       [
41966         "Mali",
41967         "ml",
41968         "223"
41969       ],
41970       [
41971         "Malta",
41972         "mt",
41973         "356"
41974       ],
41975       [
41976         "Marshall Islands",
41977         "mh",
41978         "692"
41979       ],
41980       [
41981         "Martinique",
41982         "mq",
41983         "596"
41984       ],
41985       [
41986         "Mauritania (‫موريتانيا‬‎)",
41987         "mr",
41988         "222"
41989       ],
41990       [
41991         "Mauritius (Moris)",
41992         "mu",
41993         "230"
41994       ],
41995       [
41996         "Mayotte",
41997         "yt",
41998         "262",
41999         1
42000       ],
42001       [
42002         "Mexico (México)",
42003         "mx",
42004         "52"
42005       ],
42006       [
42007         "Micronesia",
42008         "fm",
42009         "691"
42010       ],
42011       [
42012         "Moldova (Republica Moldova)",
42013         "md",
42014         "373"
42015       ],
42016       [
42017         "Monaco",
42018         "mc",
42019         "377"
42020       ],
42021       [
42022         "Mongolia (Монгол)",
42023         "mn",
42024         "976"
42025       ],
42026       [
42027         "Montenegro (Crna Gora)",
42028         "me",
42029         "382"
42030       ],
42031       [
42032         "Montserrat",
42033         "ms",
42034         "1664"
42035       ],
42036       [
42037         "Morocco (‫المغرب‬‎)",
42038         "ma",
42039         "212",
42040         0
42041       ],
42042       [
42043         "Mozambique (Moçambique)",
42044         "mz",
42045         "258"
42046       ],
42047       [
42048         "Myanmar (Burma) (မြန်မာ)",
42049         "mm",
42050         "95"
42051       ],
42052       [
42053         "Namibia (Namibië)",
42054         "na",
42055         "264"
42056       ],
42057       [
42058         "Nauru",
42059         "nr",
42060         "674"
42061       ],
42062       [
42063         "Nepal (नेपाल)",
42064         "np",
42065         "977"
42066       ],
42067       [
42068         "Netherlands (Nederland)",
42069         "nl",
42070         "31"
42071       ],
42072       [
42073         "New Caledonia (Nouvelle-Calédonie)",
42074         "nc",
42075         "687"
42076       ],
42077       [
42078         "New Zealand",
42079         "nz",
42080         "64"
42081       ],
42082       [
42083         "Nicaragua",
42084         "ni",
42085         "505"
42086       ],
42087       [
42088         "Niger (Nijar)",
42089         "ne",
42090         "227"
42091       ],
42092       [
42093         "Nigeria",
42094         "ng",
42095         "234"
42096       ],
42097       [
42098         "Niue",
42099         "nu",
42100         "683"
42101       ],
42102       [
42103         "Norfolk Island",
42104         "nf",
42105         "672"
42106       ],
42107       [
42108         "North Korea (조선 민주주의 인민 공화국)",
42109         "kp",
42110         "850"
42111       ],
42112       [
42113         "Northern Mariana Islands",
42114         "mp",
42115         "1670"
42116       ],
42117       [
42118         "Norway (Norge)",
42119         "no",
42120         "47",
42121         0
42122       ],
42123       [
42124         "Oman (‫عُمان‬‎)",
42125         "om",
42126         "968"
42127       ],
42128       [
42129         "Pakistan (‫پاکستان‬‎)",
42130         "pk",
42131         "92"
42132       ],
42133       [
42134         "Palau",
42135         "pw",
42136         "680"
42137       ],
42138       [
42139         "Palestine (‫فلسطين‬‎)",
42140         "ps",
42141         "970"
42142       ],
42143       [
42144         "Panama (Panamá)",
42145         "pa",
42146         "507"
42147       ],
42148       [
42149         "Papua New Guinea",
42150         "pg",
42151         "675"
42152       ],
42153       [
42154         "Paraguay",
42155         "py",
42156         "595"
42157       ],
42158       [
42159         "Peru (Perú)",
42160         "pe",
42161         "51"
42162       ],
42163       [
42164         "Philippines",
42165         "ph",
42166         "63"
42167       ],
42168       [
42169         "Poland (Polska)",
42170         "pl",
42171         "48"
42172       ],
42173       [
42174         "Portugal",
42175         "pt",
42176         "351"
42177       ],
42178       [
42179         "Puerto Rico",
42180         "pr",
42181         "1",
42182         3,
42183         ["787", "939"]
42184       ],
42185       [
42186         "Qatar (‫قطر‬‎)",
42187         "qa",
42188         "974"
42189       ],
42190       [
42191         "Réunion (La Réunion)",
42192         "re",
42193         "262",
42194         0
42195       ],
42196       [
42197         "Romania (România)",
42198         "ro",
42199         "40"
42200       ],
42201       [
42202         "Russia (Россия)",
42203         "ru",
42204         "7",
42205         0
42206       ],
42207       [
42208         "Rwanda",
42209         "rw",
42210         "250"
42211       ],
42212       [
42213         "Saint Barthélemy",
42214         "bl",
42215         "590",
42216         1
42217       ],
42218       [
42219         "Saint Helena",
42220         "sh",
42221         "290"
42222       ],
42223       [
42224         "Saint Kitts and Nevis",
42225         "kn",
42226         "1869"
42227       ],
42228       [
42229         "Saint Lucia",
42230         "lc",
42231         "1758"
42232       ],
42233       [
42234         "Saint Martin (Saint-Martin (partie française))",
42235         "mf",
42236         "590",
42237         2
42238       ],
42239       [
42240         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42241         "pm",
42242         "508"
42243       ],
42244       [
42245         "Saint Vincent and the Grenadines",
42246         "vc",
42247         "1784"
42248       ],
42249       [
42250         "Samoa",
42251         "ws",
42252         "685"
42253       ],
42254       [
42255         "San Marino",
42256         "sm",
42257         "378"
42258       ],
42259       [
42260         "São Tomé and Príncipe (São Tomé e Príncipe)",
42261         "st",
42262         "239"
42263       ],
42264       [
42265         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42266         "sa",
42267         "966"
42268       ],
42269       [
42270         "Senegal (Sénégal)",
42271         "sn",
42272         "221"
42273       ],
42274       [
42275         "Serbia (Србија)",
42276         "rs",
42277         "381"
42278       ],
42279       [
42280         "Seychelles",
42281         "sc",
42282         "248"
42283       ],
42284       [
42285         "Sierra Leone",
42286         "sl",
42287         "232"
42288       ],
42289       [
42290         "Singapore",
42291         "sg",
42292         "65"
42293       ],
42294       [
42295         "Sint Maarten",
42296         "sx",
42297         "1721"
42298       ],
42299       [
42300         "Slovakia (Slovensko)",
42301         "sk",
42302         "421"
42303       ],
42304       [
42305         "Slovenia (Slovenija)",
42306         "si",
42307         "386"
42308       ],
42309       [
42310         "Solomon Islands",
42311         "sb",
42312         "677"
42313       ],
42314       [
42315         "Somalia (Soomaaliya)",
42316         "so",
42317         "252"
42318       ],
42319       [
42320         "South Africa",
42321         "za",
42322         "27"
42323       ],
42324       [
42325         "South Korea (대한민국)",
42326         "kr",
42327         "82"
42328       ],
42329       [
42330         "South Sudan (‫جنوب السودان‬‎)",
42331         "ss",
42332         "211"
42333       ],
42334       [
42335         "Spain (España)",
42336         "es",
42337         "34"
42338       ],
42339       [
42340         "Sri Lanka (ශ්‍රී ලංකාව)",
42341         "lk",
42342         "94"
42343       ],
42344       [
42345         "Sudan (‫السودان‬‎)",
42346         "sd",
42347         "249"
42348       ],
42349       [
42350         "Suriname",
42351         "sr",
42352         "597"
42353       ],
42354       [
42355         "Svalbard and Jan Mayen",
42356         "sj",
42357         "47",
42358         1
42359       ],
42360       [
42361         "Swaziland",
42362         "sz",
42363         "268"
42364       ],
42365       [
42366         "Sweden (Sverige)",
42367         "se",
42368         "46"
42369       ],
42370       [
42371         "Switzerland (Schweiz)",
42372         "ch",
42373         "41"
42374       ],
42375       [
42376         "Syria (‫سوريا‬‎)",
42377         "sy",
42378         "963"
42379       ],
42380       [
42381         "Taiwan (台灣)",
42382         "tw",
42383         "886"
42384       ],
42385       [
42386         "Tajikistan",
42387         "tj",
42388         "992"
42389       ],
42390       [
42391         "Tanzania",
42392         "tz",
42393         "255"
42394       ],
42395       [
42396         "Thailand (ไทย)",
42397         "th",
42398         "66"
42399       ],
42400       [
42401         "Timor-Leste",
42402         "tl",
42403         "670"
42404       ],
42405       [
42406         "Togo",
42407         "tg",
42408         "228"
42409       ],
42410       [
42411         "Tokelau",
42412         "tk",
42413         "690"
42414       ],
42415       [
42416         "Tonga",
42417         "to",
42418         "676"
42419       ],
42420       [
42421         "Trinidad and Tobago",
42422         "tt",
42423         "1868"
42424       ],
42425       [
42426         "Tunisia (‫تونس‬‎)",
42427         "tn",
42428         "216"
42429       ],
42430       [
42431         "Turkey (Türkiye)",
42432         "tr",
42433         "90"
42434       ],
42435       [
42436         "Turkmenistan",
42437         "tm",
42438         "993"
42439       ],
42440       [
42441         "Turks and Caicos Islands",
42442         "tc",
42443         "1649"
42444       ],
42445       [
42446         "Tuvalu",
42447         "tv",
42448         "688"
42449       ],
42450       [
42451         "U.S. Virgin Islands",
42452         "vi",
42453         "1340"
42454       ],
42455       [
42456         "Uganda",
42457         "ug",
42458         "256"
42459       ],
42460       [
42461         "Ukraine (Україна)",
42462         "ua",
42463         "380"
42464       ],
42465       [
42466         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42467         "ae",
42468         "971"
42469       ],
42470       [
42471         "United Kingdom",
42472         "gb",
42473         "44",
42474         0
42475       ],
42476       [
42477         "United States",
42478         "us",
42479         "1",
42480         0
42481       ],
42482       [
42483         "Uruguay",
42484         "uy",
42485         "598"
42486       ],
42487       [
42488         "Uzbekistan (Oʻzbekiston)",
42489         "uz",
42490         "998"
42491       ],
42492       [
42493         "Vanuatu",
42494         "vu",
42495         "678"
42496       ],
42497       [
42498         "Vatican City (Città del Vaticano)",
42499         "va",
42500         "39",
42501         1
42502       ],
42503       [
42504         "Venezuela",
42505         "ve",
42506         "58"
42507       ],
42508       [
42509         "Vietnam (Việt Nam)",
42510         "vn",
42511         "84"
42512       ],
42513       [
42514         "Wallis and Futuna (Wallis-et-Futuna)",
42515         "wf",
42516         "681"
42517       ],
42518       [
42519         "Western Sahara (‫الصحراء الغربية‬‎)",
42520         "eh",
42521         "212",
42522         1
42523       ],
42524       [
42525         "Yemen (‫اليمن‬‎)",
42526         "ye",
42527         "967"
42528       ],
42529       [
42530         "Zambia",
42531         "zm",
42532         "260"
42533       ],
42534       [
42535         "Zimbabwe",
42536         "zw",
42537         "263"
42538       ],
42539       [
42540         "Åland Islands",
42541         "ax",
42542         "358",
42543         1
42544       ]
42545   ];
42546   
42547   return d;
42548 }/**
42549 *    This script refer to:
42550 *    Title: International Telephone Input
42551 *    Author: Jack O'Connor
42552 *    Code version:  v12.1.12
42553 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42554 **/
42555
42556 /**
42557  * @class Roo.bootstrap.PhoneInput
42558  * @extends Roo.bootstrap.TriggerField
42559  * An input with International dial-code selection
42560  
42561  * @cfg {String} defaultDialCode default '+852'
42562  * @cfg {Array} preferedCountries default []
42563   
42564  * @constructor
42565  * Create a new PhoneInput.
42566  * @param {Object} config Configuration options
42567  */
42568
42569 Roo.bootstrap.PhoneInput = function(config) {
42570     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42571 };
42572
42573 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42574         
42575         listWidth: undefined,
42576         
42577         selectedClass: 'active',
42578         
42579         invalidClass : "has-warning",
42580         
42581         validClass: 'has-success',
42582         
42583         allowed: '0123456789',
42584         
42585         max_length: 15,
42586         
42587         /**
42588          * @cfg {String} defaultDialCode The default dial code when initializing the input
42589          */
42590         defaultDialCode: '+852',
42591         
42592         /**
42593          * @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
42594          */
42595         preferedCountries: false,
42596         
42597         getAutoCreate : function()
42598         {
42599             var data = Roo.bootstrap.PhoneInputData();
42600             var align = this.labelAlign || this.parentLabelAlign();
42601             var id = Roo.id();
42602             
42603             this.allCountries = [];
42604             this.dialCodeMapping = [];
42605             
42606             for (var i = 0; i < data.length; i++) {
42607               var c = data[i];
42608               this.allCountries[i] = {
42609                 name: c[0],
42610                 iso2: c[1],
42611                 dialCode: c[2],
42612                 priority: c[3] || 0,
42613                 areaCodes: c[4] || null
42614               };
42615               this.dialCodeMapping[c[2]] = {
42616                   name: c[0],
42617                   iso2: c[1],
42618                   priority: c[3] || 0,
42619                   areaCodes: c[4] || null
42620               };
42621             }
42622             
42623             var cfg = {
42624                 cls: 'form-group',
42625                 cn: []
42626             };
42627             
42628             var input =  {
42629                 tag: 'input',
42630                 id : id,
42631                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42632                 maxlength: this.max_length,
42633                 cls : 'form-control tel-input',
42634                 autocomplete: 'new-password'
42635             };
42636             
42637             var hiddenInput = {
42638                 tag: 'input',
42639                 type: 'hidden',
42640                 cls: 'hidden-tel-input'
42641             };
42642             
42643             if (this.name) {
42644                 hiddenInput.name = this.name;
42645             }
42646             
42647             if (this.disabled) {
42648                 input.disabled = true;
42649             }
42650             
42651             var flag_container = {
42652                 tag: 'div',
42653                 cls: 'flag-box',
42654                 cn: [
42655                     {
42656                         tag: 'div',
42657                         cls: 'flag'
42658                     },
42659                     {
42660                         tag: 'div',
42661                         cls: 'caret'
42662                     }
42663                 ]
42664             };
42665             
42666             var box = {
42667                 tag: 'div',
42668                 cls: this.hasFeedback ? 'has-feedback' : '',
42669                 cn: [
42670                     hiddenInput,
42671                     input,
42672                     {
42673                         tag: 'input',
42674                         cls: 'dial-code-holder',
42675                         disabled: true
42676                     }
42677                 ]
42678             };
42679             
42680             var container = {
42681                 cls: 'roo-select2-container input-group',
42682                 cn: [
42683                     flag_container,
42684                     box
42685                 ]
42686             };
42687             
42688             if (this.fieldLabel.length) {
42689                 var indicator = {
42690                     tag: 'i',
42691                     tooltip: 'This field is required'
42692                 };
42693                 
42694                 var label = {
42695                     tag: 'label',
42696                     'for':  id,
42697                     cls: 'control-label',
42698                     cn: []
42699                 };
42700                 
42701                 var label_text = {
42702                     tag: 'span',
42703                     html: this.fieldLabel
42704                 };
42705                 
42706                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42707                 label.cn = [
42708                     indicator,
42709                     label_text
42710                 ];
42711                 
42712                 if(this.indicatorpos == 'right') {
42713                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42714                     label.cn = [
42715                         label_text,
42716                         indicator
42717                     ];
42718                 }
42719                 
42720                 if(align == 'left') {
42721                     container = {
42722                         tag: 'div',
42723                         cn: [
42724                             container
42725                         ]
42726                     };
42727                     
42728                     if(this.labelWidth > 12){
42729                         label.style = "width: " + this.labelWidth + 'px';
42730                     }
42731                     if(this.labelWidth < 13 && this.labelmd == 0){
42732                         this.labelmd = this.labelWidth;
42733                     }
42734                     if(this.labellg > 0){
42735                         label.cls += ' col-lg-' + this.labellg;
42736                         input.cls += ' col-lg-' + (12 - this.labellg);
42737                     }
42738                     if(this.labelmd > 0){
42739                         label.cls += ' col-md-' + this.labelmd;
42740                         container.cls += ' col-md-' + (12 - this.labelmd);
42741                     }
42742                     if(this.labelsm > 0){
42743                         label.cls += ' col-sm-' + this.labelsm;
42744                         container.cls += ' col-sm-' + (12 - this.labelsm);
42745                     }
42746                     if(this.labelxs > 0){
42747                         label.cls += ' col-xs-' + this.labelxs;
42748                         container.cls += ' col-xs-' + (12 - this.labelxs);
42749                     }
42750                 }
42751             }
42752             
42753             cfg.cn = [
42754                 label,
42755                 container
42756             ];
42757             
42758             var settings = this;
42759             
42760             ['xs','sm','md','lg'].map(function(size){
42761                 if (settings[size]) {
42762                     cfg.cls += ' col-' + size + '-' + settings[size];
42763                 }
42764             });
42765             
42766             this.store = new Roo.data.Store({
42767                 proxy : new Roo.data.MemoryProxy({}),
42768                 reader : new Roo.data.JsonReader({
42769                     fields : [
42770                         {
42771                             'name' : 'name',
42772                             'type' : 'string'
42773                         },
42774                         {
42775                             'name' : 'iso2',
42776                             'type' : 'string'
42777                         },
42778                         {
42779                             'name' : 'dialCode',
42780                             'type' : 'string'
42781                         },
42782                         {
42783                             'name' : 'priority',
42784                             'type' : 'string'
42785                         },
42786                         {
42787                             'name' : 'areaCodes',
42788                             'type' : 'string'
42789                         }
42790                     ]
42791                 })
42792             });
42793             
42794             if(!this.preferedCountries) {
42795                 this.preferedCountries = [
42796                     'hk',
42797                     'gb',
42798                     'us'
42799                 ];
42800             }
42801             
42802             var p = this.preferedCountries.reverse();
42803             
42804             if(p) {
42805                 for (var i = 0; i < p.length; i++) {
42806                     for (var j = 0; j < this.allCountries.length; j++) {
42807                         if(this.allCountries[j].iso2 == p[i]) {
42808                             var t = this.allCountries[j];
42809                             this.allCountries.splice(j,1);
42810                             this.allCountries.unshift(t);
42811                         }
42812                     } 
42813                 }
42814             }
42815             
42816             this.store.proxy.data = {
42817                 success: true,
42818                 data: this.allCountries
42819             };
42820             
42821             return cfg;
42822         },
42823         
42824         initEvents : function()
42825         {
42826             this.createList();
42827             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42828             
42829             this.indicator = this.indicatorEl();
42830             this.flag = this.flagEl();
42831             this.dialCodeHolder = this.dialCodeHolderEl();
42832             
42833             this.trigger = this.el.select('div.flag-box',true).first();
42834             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42835             
42836             var _this = this;
42837             
42838             (function(){
42839                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42840                 _this.list.setWidth(lw);
42841             }).defer(100);
42842             
42843             this.list.on('mouseover', this.onViewOver, this);
42844             this.list.on('mousemove', this.onViewMove, this);
42845             this.inputEl().on("keyup", this.onKeyUp, this);
42846             this.inputEl().on("keypress", this.onKeyPress, this);
42847             
42848             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42849
42850             this.view = new Roo.View(this.list, this.tpl, {
42851                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42852             });
42853             
42854             this.view.on('click', this.onViewClick, this);
42855             this.setValue(this.defaultDialCode);
42856         },
42857         
42858         onTriggerClick : function(e)
42859         {
42860             Roo.log('trigger click');
42861             if(this.disabled){
42862                 return;
42863             }
42864             
42865             if(this.isExpanded()){
42866                 this.collapse();
42867                 this.hasFocus = false;
42868             }else {
42869                 this.store.load({});
42870                 this.hasFocus = true;
42871                 this.expand();
42872             }
42873         },
42874         
42875         isExpanded : function()
42876         {
42877             return this.list.isVisible();
42878         },
42879         
42880         collapse : function()
42881         {
42882             if(!this.isExpanded()){
42883                 return;
42884             }
42885             this.list.hide();
42886             Roo.get(document).un('mousedown', this.collapseIf, this);
42887             Roo.get(document).un('mousewheel', this.collapseIf, this);
42888             this.fireEvent('collapse', this);
42889             this.validate();
42890         },
42891         
42892         expand : function()
42893         {
42894             Roo.log('expand');
42895
42896             if(this.isExpanded() || !this.hasFocus){
42897                 return;
42898             }
42899             
42900             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42901             this.list.setWidth(lw);
42902             
42903             this.list.show();
42904             this.restrictHeight();
42905             
42906             Roo.get(document).on('mousedown', this.collapseIf, this);
42907             Roo.get(document).on('mousewheel', this.collapseIf, this);
42908             
42909             this.fireEvent('expand', this);
42910         },
42911         
42912         restrictHeight : function()
42913         {
42914             this.list.alignTo(this.inputEl(), this.listAlign);
42915             this.list.alignTo(this.inputEl(), this.listAlign);
42916         },
42917         
42918         onViewOver : function(e, t)
42919         {
42920             if(this.inKeyMode){
42921                 return;
42922             }
42923             var item = this.view.findItemFromChild(t);
42924             
42925             if(item){
42926                 var index = this.view.indexOf(item);
42927                 this.select(index, false);
42928             }
42929         },
42930
42931         // private
42932         onViewClick : function(view, doFocus, el, e)
42933         {
42934             var index = this.view.getSelectedIndexes()[0];
42935             
42936             var r = this.store.getAt(index);
42937             
42938             if(r){
42939                 this.onSelect(r, index);
42940             }
42941             if(doFocus !== false && !this.blockFocus){
42942                 this.inputEl().focus();
42943             }
42944         },
42945         
42946         onViewMove : function(e, t)
42947         {
42948             this.inKeyMode = false;
42949         },
42950         
42951         select : function(index, scrollIntoView)
42952         {
42953             this.selectedIndex = index;
42954             this.view.select(index);
42955             if(scrollIntoView !== false){
42956                 var el = this.view.getNode(index);
42957                 if(el){
42958                     this.list.scrollChildIntoView(el, false);
42959                 }
42960             }
42961         },
42962         
42963         createList : function()
42964         {
42965             this.list = Roo.get(document.body).createChild({
42966                 tag: 'ul',
42967                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42968                 style: 'display:none'
42969             });
42970             
42971             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42972         },
42973         
42974         collapseIf : function(e)
42975         {
42976             var in_combo  = e.within(this.el);
42977             var in_list =  e.within(this.list);
42978             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42979             
42980             if (in_combo || in_list || is_list) {
42981                 return;
42982             }
42983             this.collapse();
42984         },
42985         
42986         onSelect : function(record, index)
42987         {
42988             if(this.fireEvent('beforeselect', this, record, index) !== false){
42989                 
42990                 this.setFlagClass(record.data.iso2);
42991                 this.setDialCode(record.data.dialCode);
42992                 this.hasFocus = false;
42993                 this.collapse();
42994                 this.fireEvent('select', this, record, index);
42995             }
42996         },
42997         
42998         flagEl : function()
42999         {
43000             var flag = this.el.select('div.flag',true).first();
43001             if(!flag){
43002                 return false;
43003             }
43004             return flag;
43005         },
43006         
43007         dialCodeHolderEl : function()
43008         {
43009             var d = this.el.select('input.dial-code-holder',true).first();
43010             if(!d){
43011                 return false;
43012             }
43013             return d;
43014         },
43015         
43016         setDialCode : function(v)
43017         {
43018             this.dialCodeHolder.dom.value = '+'+v;
43019         },
43020         
43021         setFlagClass : function(n)
43022         {
43023             this.flag.dom.className = 'flag '+n;
43024         },
43025         
43026         getValue : function()
43027         {
43028             var v = this.inputEl().getValue();
43029             if(this.dialCodeHolder) {
43030                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43031             }
43032             return v;
43033         },
43034         
43035         setValue : function(v)
43036         {
43037             var d = this.getDialCode(v);
43038             
43039             //invalid dial code
43040             if(v.length == 0 || !d || d.length == 0) {
43041                 if(this.rendered){
43042                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43043                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43044                 }
43045                 return;
43046             }
43047             
43048             //valid dial code
43049             this.setFlagClass(this.dialCodeMapping[d].iso2);
43050             this.setDialCode(d);
43051             this.inputEl().dom.value = v.replace('+'+d,'');
43052             this.hiddenEl().dom.value = this.getValue();
43053             
43054             this.validate();
43055         },
43056         
43057         getDialCode : function(v)
43058         {
43059             v = v ||  '';
43060             
43061             if (v.length == 0) {
43062                 return this.dialCodeHolder.dom.value;
43063             }
43064             
43065             var dialCode = "";
43066             if (v.charAt(0) != "+") {
43067                 return false;
43068             }
43069             var numericChars = "";
43070             for (var i = 1; i < v.length; i++) {
43071               var c = v.charAt(i);
43072               if (!isNaN(c)) {
43073                 numericChars += c;
43074                 if (this.dialCodeMapping[numericChars]) {
43075                   dialCode = v.substr(1, i);
43076                 }
43077                 if (numericChars.length == 4) {
43078                   break;
43079                 }
43080               }
43081             }
43082             return dialCode;
43083         },
43084         
43085         reset : function()
43086         {
43087             this.setValue(this.defaultDialCode);
43088             this.validate();
43089         },
43090         
43091         hiddenEl : function()
43092         {
43093             return this.el.select('input.hidden-tel-input',true).first();
43094         },
43095         
43096         // after setting val
43097         onKeyUp : function(e){
43098             this.setValue(this.getValue());
43099         },
43100         
43101         onKeyPress : function(e){
43102             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43103                 e.stopEvent();
43104             }
43105         }
43106         
43107 });
43108 /**
43109  * @class Roo.bootstrap.MoneyField
43110  * @extends Roo.bootstrap.ComboBox
43111  * Bootstrap MoneyField class
43112  * 
43113  * @constructor
43114  * Create a new MoneyField.
43115  * @param {Object} config Configuration options
43116  */
43117
43118 Roo.bootstrap.MoneyField = function(config) {
43119     
43120     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43121     
43122 };
43123
43124 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43125     
43126     /**
43127      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43128      */
43129     allowDecimals : true,
43130     /**
43131      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43132      */
43133     decimalSeparator : ".",
43134     /**
43135      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43136      */
43137     decimalPrecision : 0,
43138     /**
43139      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43140      */
43141     allowNegative : true,
43142     /**
43143      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43144      */
43145     allowZero: true,
43146     /**
43147      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43148      */
43149     minValue : Number.NEGATIVE_INFINITY,
43150     /**
43151      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43152      */
43153     maxValue : Number.MAX_VALUE,
43154     /**
43155      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43156      */
43157     minText : "The minimum value for this field is {0}",
43158     /**
43159      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43160      */
43161     maxText : "The maximum value for this field is {0}",
43162     /**
43163      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43164      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43165      */
43166     nanText : "{0} is not a valid number",
43167     /**
43168      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43169      */
43170     castInt : true,
43171     /**
43172      * @cfg {String} defaults currency of the MoneyField
43173      * value should be in lkey
43174      */
43175     defaultCurrency : false,
43176     /**
43177      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43178      */
43179     thousandsDelimiter : false,
43180     /**
43181      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43182      */
43183     max_length: false,
43184     
43185     inputlg : 9,
43186     inputmd : 9,
43187     inputsm : 9,
43188     inputxs : 6,
43189     
43190     store : false,
43191     
43192     getAutoCreate : function()
43193     {
43194         var align = this.labelAlign || this.parentLabelAlign();
43195         
43196         var id = Roo.id();
43197
43198         var cfg = {
43199             cls: 'form-group',
43200             cn: []
43201         };
43202
43203         var input =  {
43204             tag: 'input',
43205             id : id,
43206             cls : 'form-control roo-money-amount-input',
43207             autocomplete: 'new-password'
43208         };
43209         
43210         var hiddenInput = {
43211             tag: 'input',
43212             type: 'hidden',
43213             id: Roo.id(),
43214             cls: 'hidden-number-input'
43215         };
43216         
43217         if(this.max_length) {
43218             input.maxlength = this.max_length; 
43219         }
43220         
43221         if (this.name) {
43222             hiddenInput.name = this.name;
43223         }
43224
43225         if (this.disabled) {
43226             input.disabled = true;
43227         }
43228
43229         var clg = 12 - this.inputlg;
43230         var cmd = 12 - this.inputmd;
43231         var csm = 12 - this.inputsm;
43232         var cxs = 12 - this.inputxs;
43233         
43234         var container = {
43235             tag : 'div',
43236             cls : 'row roo-money-field',
43237             cn : [
43238                 {
43239                     tag : 'div',
43240                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43241                     cn : [
43242                         {
43243                             tag : 'div',
43244                             cls: 'roo-select2-container input-group',
43245                             cn: [
43246                                 {
43247                                     tag : 'input',
43248                                     cls : 'form-control roo-money-currency-input',
43249                                     autocomplete: 'new-password',
43250                                     readOnly : 1,
43251                                     name : this.currencyName
43252                                 },
43253                                 {
43254                                     tag :'span',
43255                                     cls : 'input-group-addon',
43256                                     cn : [
43257                                         {
43258                                             tag: 'span',
43259                                             cls: 'caret'
43260                                         }
43261                                     ]
43262                                 }
43263                             ]
43264                         }
43265                     ]
43266                 },
43267                 {
43268                     tag : 'div',
43269                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43270                     cn : [
43271                         {
43272                             tag: 'div',
43273                             cls: this.hasFeedback ? 'has-feedback' : '',
43274                             cn: [
43275                                 input
43276                             ]
43277                         }
43278                     ]
43279                 }
43280             ]
43281             
43282         };
43283         
43284         if (this.fieldLabel.length) {
43285             var indicator = {
43286                 tag: 'i',
43287                 tooltip: 'This field is required'
43288             };
43289
43290             var label = {
43291                 tag: 'label',
43292                 'for':  id,
43293                 cls: 'control-label',
43294                 cn: []
43295             };
43296
43297             var label_text = {
43298                 tag: 'span',
43299                 html: this.fieldLabel
43300             };
43301
43302             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43303             label.cn = [
43304                 indicator,
43305                 label_text
43306             ];
43307
43308             if(this.indicatorpos == 'right') {
43309                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43310                 label.cn = [
43311                     label_text,
43312                     indicator
43313                 ];
43314             }
43315
43316             if(align == 'left') {
43317                 container = {
43318                     tag: 'div',
43319                     cn: [
43320                         container
43321                     ]
43322                 };
43323
43324                 if(this.labelWidth > 12){
43325                     label.style = "width: " + this.labelWidth + 'px';
43326                 }
43327                 if(this.labelWidth < 13 && this.labelmd == 0){
43328                     this.labelmd = this.labelWidth;
43329                 }
43330                 if(this.labellg > 0){
43331                     label.cls += ' col-lg-' + this.labellg;
43332                     input.cls += ' col-lg-' + (12 - this.labellg);
43333                 }
43334                 if(this.labelmd > 0){
43335                     label.cls += ' col-md-' + this.labelmd;
43336                     container.cls += ' col-md-' + (12 - this.labelmd);
43337                 }
43338                 if(this.labelsm > 0){
43339                     label.cls += ' col-sm-' + this.labelsm;
43340                     container.cls += ' col-sm-' + (12 - this.labelsm);
43341                 }
43342                 if(this.labelxs > 0){
43343                     label.cls += ' col-xs-' + this.labelxs;
43344                     container.cls += ' col-xs-' + (12 - this.labelxs);
43345                 }
43346             }
43347         }
43348
43349         cfg.cn = [
43350             label,
43351             container,
43352             hiddenInput
43353         ];
43354         
43355         var settings = this;
43356
43357         ['xs','sm','md','lg'].map(function(size){
43358             if (settings[size]) {
43359                 cfg.cls += ' col-' + size + '-' + settings[size];
43360             }
43361         });
43362         
43363         return cfg;
43364     },
43365     
43366     initEvents : function()
43367     {
43368         this.indicator = this.indicatorEl();
43369         
43370         this.initCurrencyEvent();
43371         
43372         this.initNumberEvent();
43373     },
43374     
43375     initCurrencyEvent : function()
43376     {
43377         if (!this.store) {
43378             throw "can not find store for combo";
43379         }
43380         
43381         this.store = Roo.factory(this.store, Roo.data);
43382         this.store.parent = this;
43383         
43384         this.createList();
43385         
43386         this.triggerEl = this.el.select('.input-group-addon', true).first();
43387         
43388         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43389         
43390         var _this = this;
43391         
43392         (function(){
43393             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43394             _this.list.setWidth(lw);
43395         }).defer(100);
43396         
43397         this.list.on('mouseover', this.onViewOver, this);
43398         this.list.on('mousemove', this.onViewMove, this);
43399         this.list.on('scroll', this.onViewScroll, this);
43400         
43401         if(!this.tpl){
43402             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43403         }
43404         
43405         this.view = new Roo.View(this.list, this.tpl, {
43406             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43407         });
43408         
43409         this.view.on('click', this.onViewClick, this);
43410         
43411         this.store.on('beforeload', this.onBeforeLoad, this);
43412         this.store.on('load', this.onLoad, this);
43413         this.store.on('loadexception', this.onLoadException, this);
43414         
43415         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43416             "up" : function(e){
43417                 this.inKeyMode = true;
43418                 this.selectPrev();
43419             },
43420
43421             "down" : function(e){
43422                 if(!this.isExpanded()){
43423                     this.onTriggerClick();
43424                 }else{
43425                     this.inKeyMode = true;
43426                     this.selectNext();
43427                 }
43428             },
43429
43430             "enter" : function(e){
43431                 this.collapse();
43432                 
43433                 if(this.fireEvent("specialkey", this, e)){
43434                     this.onViewClick(false);
43435                 }
43436                 
43437                 return true;
43438             },
43439
43440             "esc" : function(e){
43441                 this.collapse();
43442             },
43443
43444             "tab" : function(e){
43445                 this.collapse();
43446                 
43447                 if(this.fireEvent("specialkey", this, e)){
43448                     this.onViewClick(false);
43449                 }
43450                 
43451                 return true;
43452             },
43453
43454             scope : this,
43455
43456             doRelay : function(foo, bar, hname){
43457                 if(hname == 'down' || this.scope.isExpanded()){
43458                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43459                 }
43460                 return true;
43461             },
43462
43463             forceKeyDown: true
43464         });
43465         
43466         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43467         
43468     },
43469     
43470     initNumberEvent : function(e)
43471     {
43472         this.inputEl().on("keydown" , this.fireKey,  this);
43473         this.inputEl().on("focus", this.onFocus,  this);
43474         this.inputEl().on("blur", this.onBlur,  this);
43475         
43476         this.inputEl().relayEvent('keyup', this);
43477         
43478         if(this.indicator){
43479             this.indicator.addClass('invisible');
43480         }
43481  
43482         this.originalValue = this.getValue();
43483         
43484         if(this.validationEvent == 'keyup'){
43485             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43486             this.inputEl().on('keyup', this.filterValidation, this);
43487         }
43488         else if(this.validationEvent !== false){
43489             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43490         }
43491         
43492         if(this.selectOnFocus){
43493             this.on("focus", this.preFocus, this);
43494             
43495         }
43496         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43497             this.inputEl().on("keypress", this.filterKeys, this);
43498         } else {
43499             this.inputEl().relayEvent('keypress', this);
43500         }
43501         
43502         var allowed = "0123456789";
43503         
43504         if(this.allowDecimals){
43505             allowed += this.decimalSeparator;
43506         }
43507         
43508         if(this.allowNegative){
43509             allowed += "-";
43510         }
43511         
43512         if(this.thousandsDelimiter) {
43513             allowed += ",";
43514         }
43515         
43516         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43517         
43518         var keyPress = function(e){
43519             
43520             var k = e.getKey();
43521             
43522             var c = e.getCharCode();
43523             
43524             if(
43525                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43526                     allowed.indexOf(String.fromCharCode(c)) === -1
43527             ){
43528                 e.stopEvent();
43529                 return;
43530             }
43531             
43532             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43533                 return;
43534             }
43535             
43536             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43537                 e.stopEvent();
43538             }
43539         };
43540         
43541         this.inputEl().on("keypress", keyPress, this);
43542         
43543     },
43544     
43545     onTriggerClick : function(e)
43546     {   
43547         if(this.disabled){
43548             return;
43549         }
43550         
43551         this.page = 0;
43552         this.loadNext = false;
43553         
43554         if(this.isExpanded()){
43555             this.collapse();
43556             return;
43557         }
43558         
43559         this.hasFocus = true;
43560         
43561         if(this.triggerAction == 'all') {
43562             this.doQuery(this.allQuery, true);
43563             return;
43564         }
43565         
43566         this.doQuery(this.getRawValue());
43567     },
43568     
43569     getCurrency : function()
43570     {   
43571         var v = this.currencyEl().getValue();
43572         
43573         return v;
43574     },
43575     
43576     restrictHeight : function()
43577     {
43578         this.list.alignTo(this.currencyEl(), this.listAlign);
43579         this.list.alignTo(this.currencyEl(), this.listAlign);
43580     },
43581     
43582     onViewClick : function(view, doFocus, el, e)
43583     {
43584         var index = this.view.getSelectedIndexes()[0];
43585         
43586         var r = this.store.getAt(index);
43587         
43588         if(r){
43589             this.onSelect(r, index);
43590         }
43591     },
43592     
43593     onSelect : function(record, index){
43594         
43595         if(this.fireEvent('beforeselect', this, record, index) !== false){
43596         
43597             this.setFromCurrencyData(index > -1 ? record.data : false);
43598             
43599             this.collapse();
43600             
43601             this.fireEvent('select', this, record, index);
43602         }
43603     },
43604     
43605     setFromCurrencyData : function(o)
43606     {
43607         var currency = '';
43608         
43609         this.lastCurrency = o;
43610         
43611         if (this.currencyField) {
43612             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43613         } else {
43614             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43615         }
43616         
43617         this.lastSelectionText = currency;
43618         
43619         //setting default currency
43620         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43621             this.setCurrency(this.defaultCurrency);
43622             return;
43623         }
43624         
43625         this.setCurrency(currency);
43626     },
43627     
43628     setFromData : function(o)
43629     {
43630         var c = {};
43631         
43632         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43633         
43634         this.setFromCurrencyData(c);
43635         
43636         var value = '';
43637         
43638         if (this.name) {
43639             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43640         } else {
43641             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43642         }
43643         
43644         this.setValue(value);
43645         
43646     },
43647     
43648     setCurrency : function(v)
43649     {   
43650         this.currencyValue = v;
43651         
43652         if(this.rendered){
43653             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43654             this.validate();
43655         }
43656     },
43657     
43658     setValue : function(v)
43659     {
43660         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43661         
43662         this.value = v;
43663         
43664         if(this.rendered){
43665             
43666             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43667             
43668             this.inputEl().dom.value = (v == '') ? '' :
43669                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43670             
43671             if(!this.allowZero && v === '0') {
43672                 this.hiddenEl().dom.value = '';
43673                 this.inputEl().dom.value = '';
43674             }
43675             
43676             this.validate();
43677         }
43678     },
43679     
43680     getRawValue : function()
43681     {
43682         var v = this.inputEl().getValue();
43683         
43684         return v;
43685     },
43686     
43687     getValue : function()
43688     {
43689         return this.fixPrecision(this.parseValue(this.getRawValue()));
43690     },
43691     
43692     parseValue : function(value)
43693     {
43694         if(this.thousandsDelimiter) {
43695             value += "";
43696             r = new RegExp(",", "g");
43697             value = value.replace(r, "");
43698         }
43699         
43700         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43701         return isNaN(value) ? '' : value;
43702         
43703     },
43704     
43705     fixPrecision : function(value)
43706     {
43707         if(this.thousandsDelimiter) {
43708             value += "";
43709             r = new RegExp(",", "g");
43710             value = value.replace(r, "");
43711         }
43712         
43713         var nan = isNaN(value);
43714         
43715         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43716             return nan ? '' : value;
43717         }
43718         return parseFloat(value).toFixed(this.decimalPrecision);
43719     },
43720     
43721     decimalPrecisionFcn : function(v)
43722     {
43723         return Math.floor(v);
43724     },
43725     
43726     validateValue : function(value)
43727     {
43728         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43729             return false;
43730         }
43731         
43732         var num = this.parseValue(value);
43733         
43734         if(isNaN(num)){
43735             this.markInvalid(String.format(this.nanText, value));
43736             return false;
43737         }
43738         
43739         if(num < this.minValue){
43740             this.markInvalid(String.format(this.minText, this.minValue));
43741             return false;
43742         }
43743         
43744         if(num > this.maxValue){
43745             this.markInvalid(String.format(this.maxText, this.maxValue));
43746             return false;
43747         }
43748         
43749         return true;
43750     },
43751     
43752     validate : function()
43753     {
43754         if(this.disabled || this.allowBlank){
43755             this.markValid();
43756             return true;
43757         }
43758         
43759         var currency = this.getCurrency();
43760         
43761         if(this.validateValue(this.getRawValue()) && currency.length){
43762             this.markValid();
43763             return true;
43764         }
43765         
43766         this.markInvalid();
43767         return false;
43768     },
43769     
43770     getName: function()
43771     {
43772         return this.name;
43773     },
43774     
43775     beforeBlur : function()
43776     {
43777         if(!this.castInt){
43778             return;
43779         }
43780         
43781         var v = this.parseValue(this.getRawValue());
43782         
43783         if(v || v == 0){
43784             this.setValue(v);
43785         }
43786     },
43787     
43788     onBlur : function()
43789     {
43790         this.beforeBlur();
43791         
43792         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43793             //this.el.removeClass(this.focusClass);
43794         }
43795         
43796         this.hasFocus = false;
43797         
43798         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43799             this.validate();
43800         }
43801         
43802         var v = this.getValue();
43803         
43804         if(String(v) !== String(this.startValue)){
43805             this.fireEvent('change', this, v, this.startValue);
43806         }
43807         
43808         this.fireEvent("blur", this);
43809     },
43810     
43811     inputEl : function()
43812     {
43813         return this.el.select('.roo-money-amount-input', true).first();
43814     },
43815     
43816     currencyEl : function()
43817     {
43818         return this.el.select('.roo-money-currency-input', true).first();
43819     },
43820     
43821     hiddenEl : function()
43822     {
43823         return this.el.select('input.hidden-number-input',true).first();
43824     }
43825     
43826 });/**
43827  * @class Roo.bootstrap.BezierSignature
43828  * @extends Roo.bootstrap.Component
43829  * Bootstrap BezierSignature class
43830  * This script refer to:
43831  *    Title: Signature Pad
43832  *    Author: szimek
43833  *    Availability: https://github.com/szimek/signature_pad
43834  *
43835  * @constructor
43836  * Create a new BezierSignature
43837  * @param {Object} config The config object
43838  */
43839
43840 Roo.bootstrap.BezierSignature = function(config){
43841     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43842     this.addEvents({
43843         "resize" : true
43844     });
43845 };
43846
43847 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43848 {
43849      
43850     curve_data: [],
43851     
43852     is_empty: true,
43853     
43854     mouse_btn_down: true,
43855     
43856     /**
43857      * @cfg {int} canvas height
43858      */
43859     canvas_height: '200px',
43860     
43861     /**
43862      * @cfg {float|function} Radius of a single dot.
43863      */ 
43864     dot_size: false,
43865     
43866     /**
43867      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43868      */
43869     min_width: 0.5,
43870     
43871     /**
43872      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43873      */
43874     max_width: 2.5,
43875     
43876     /**
43877      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43878      */
43879     throttle: 16,
43880     
43881     /**
43882      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43883      */
43884     min_distance: 5,
43885     
43886     /**
43887      * @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.
43888      */
43889     bg_color: 'rgba(0, 0, 0, 0)',
43890     
43891     /**
43892      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43893      */
43894     dot_color: 'black',
43895     
43896     /**
43897      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43898      */ 
43899     velocity_filter_weight: 0.7,
43900     
43901     /**
43902      * @cfg {function} Callback when stroke begin. 
43903      */
43904     onBegin: false,
43905     
43906     /**
43907      * @cfg {function} Callback when stroke end.
43908      */
43909     onEnd: false,
43910     
43911     getAutoCreate : function()
43912     {
43913         var cls = 'roo-signature column';
43914         
43915         if(this.cls){
43916             cls += ' ' + this.cls;
43917         }
43918         
43919         var col_sizes = [
43920             'lg',
43921             'md',
43922             'sm',
43923             'xs'
43924         ];
43925         
43926         for(var i = 0; i < col_sizes.length; i++) {
43927             if(this[col_sizes[i]]) {
43928                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43929             }
43930         }
43931         
43932         var cfg = {
43933             tag: 'div',
43934             cls: cls,
43935             cn: [
43936                 {
43937                     tag: 'div',
43938                     cls: 'roo-signature-body',
43939                     cn: [
43940                         {
43941                             tag: 'canvas',
43942                             cls: 'roo-signature-body-canvas',
43943                             height: this.canvas_height,
43944                             width: this.canvas_width
43945                         }
43946                     ]
43947                 },
43948                 {
43949                     tag: 'input',
43950                     type: 'file',
43951                     style: 'display: none'
43952                 }
43953             ]
43954         };
43955         
43956         return cfg;
43957     },
43958     
43959     initEvents: function() 
43960     {
43961         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43962         
43963         var canvas = this.canvasEl();
43964         
43965         // mouse && touch event swapping...
43966         canvas.dom.style.touchAction = 'none';
43967         canvas.dom.style.msTouchAction = 'none';
43968         
43969         this.mouse_btn_down = false;
43970         canvas.on('mousedown', this._handleMouseDown, this);
43971         canvas.on('mousemove', this._handleMouseMove, this);
43972         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43973         
43974         if (window.PointerEvent) {
43975             canvas.on('pointerdown', this._handleMouseDown, this);
43976             canvas.on('pointermove', this._handleMouseMove, this);
43977             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43978         }
43979         
43980         if ('ontouchstart' in window) {
43981             canvas.on('touchstart', this._handleTouchStart, this);
43982             canvas.on('touchmove', this._handleTouchMove, this);
43983             canvas.on('touchend', this._handleTouchEnd, this);
43984         }
43985         
43986         Roo.EventManager.onWindowResize(this.resize, this, true);
43987         
43988         // file input event
43989         this.fileEl().on('change', this.uploadImage, this);
43990         
43991         this.clear();
43992         
43993         this.resize();
43994     },
43995     
43996     resize: function(){
43997         
43998         var canvas = this.canvasEl().dom;
43999         var ctx = this.canvasElCtx();
44000         var img_data = false;
44001         
44002         if(canvas.width > 0) {
44003             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44004         }
44005         // setting canvas width will clean img data
44006         canvas.width = 0;
44007         
44008         var style = window.getComputedStyle ? 
44009             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44010             
44011         var padding_left = parseInt(style.paddingLeft) || 0;
44012         var padding_right = parseInt(style.paddingRight) || 0;
44013         
44014         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44015         
44016         if(img_data) {
44017             ctx.putImageData(img_data, 0, 0);
44018         }
44019     },
44020     
44021     _handleMouseDown: function(e)
44022     {
44023         if (e.browserEvent.which === 1) {
44024             this.mouse_btn_down = true;
44025             this.strokeBegin(e);
44026         }
44027     },
44028     
44029     _handleMouseMove: function (e)
44030     {
44031         if (this.mouse_btn_down) {
44032             this.strokeMoveUpdate(e);
44033         }
44034     },
44035     
44036     _handleMouseUp: function (e)
44037     {
44038         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44039             this.mouse_btn_down = false;
44040             this.strokeEnd(e);
44041         }
44042     },
44043     
44044     _handleTouchStart: function (e) {
44045         
44046         e.preventDefault();
44047         if (e.browserEvent.targetTouches.length === 1) {
44048             // var touch = e.browserEvent.changedTouches[0];
44049             // this.strokeBegin(touch);
44050             
44051              this.strokeBegin(e); // assume e catching the correct xy...
44052         }
44053     },
44054     
44055     _handleTouchMove: function (e) {
44056         e.preventDefault();
44057         // var touch = event.targetTouches[0];
44058         // _this._strokeMoveUpdate(touch);
44059         this.strokeMoveUpdate(e);
44060     },
44061     
44062     _handleTouchEnd: function (e) {
44063         var wasCanvasTouched = e.target === this.canvasEl().dom;
44064         if (wasCanvasTouched) {
44065             e.preventDefault();
44066             // var touch = event.changedTouches[0];
44067             // _this._strokeEnd(touch);
44068             this.strokeEnd(e);
44069         }
44070     },
44071     
44072     reset: function () {
44073         this._lastPoints = [];
44074         this._lastVelocity = 0;
44075         this._lastWidth = (this.min_width + this.max_width) / 2;
44076         this.canvasElCtx().fillStyle = this.dot_color;
44077     },
44078     
44079     strokeMoveUpdate: function(e)
44080     {
44081         this.strokeUpdate(e);
44082         
44083         if (this.throttle) {
44084             this.throttleStroke(this.strokeUpdate, this.throttle);
44085         }
44086         else {
44087             this.strokeUpdate(e);
44088         }
44089     },
44090     
44091     strokeBegin: function(e)
44092     {
44093         var newPointGroup = {
44094             color: this.dot_color,
44095             points: []
44096         };
44097         
44098         if (typeof this.onBegin === 'function') {
44099             this.onBegin(e);
44100         }
44101         
44102         this.curve_data.push(newPointGroup);
44103         this.reset();
44104         this.strokeUpdate(e);
44105     },
44106     
44107     strokeUpdate: function(e)
44108     {
44109         var rect = this.canvasEl().dom.getBoundingClientRect();
44110         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44111         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44112         var lastPoints = lastPointGroup.points;
44113         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44114         var isLastPointTooClose = lastPoint
44115             ? point.distanceTo(lastPoint) <= this.min_distance
44116             : false;
44117         var color = lastPointGroup.color;
44118         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44119             var curve = this.addPoint(point);
44120             if (!lastPoint) {
44121                 this.drawDot({color: color, point: point});
44122             }
44123             else if (curve) {
44124                 this.drawCurve({color: color, curve: curve});
44125             }
44126             lastPoints.push({
44127                 time: point.time,
44128                 x: point.x,
44129                 y: point.y
44130             });
44131         }
44132     },
44133     
44134     strokeEnd: function(e)
44135     {
44136         this.strokeUpdate(e);
44137         if (typeof this.onEnd === 'function') {
44138             this.onEnd(e);
44139         }
44140     },
44141     
44142     addPoint:  function (point) {
44143         var _lastPoints = this._lastPoints;
44144         _lastPoints.push(point);
44145         if (_lastPoints.length > 2) {
44146             if (_lastPoints.length === 3) {
44147                 _lastPoints.unshift(_lastPoints[0]);
44148             }
44149             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44150             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44151             _lastPoints.shift();
44152             return curve;
44153         }
44154         return null;
44155     },
44156     
44157     calculateCurveWidths: function (startPoint, endPoint) {
44158         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44159             (1 - this.velocity_filter_weight) * this._lastVelocity;
44160
44161         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44162         var widths = {
44163             end: newWidth,
44164             start: this._lastWidth
44165         };
44166         
44167         this._lastVelocity = velocity;
44168         this._lastWidth = newWidth;
44169         return widths;
44170     },
44171     
44172     drawDot: function (_a) {
44173         var color = _a.color, point = _a.point;
44174         var ctx = this.canvasElCtx();
44175         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44176         ctx.beginPath();
44177         this.drawCurveSegment(point.x, point.y, width);
44178         ctx.closePath();
44179         ctx.fillStyle = color;
44180         ctx.fill();
44181     },
44182     
44183     drawCurve: function (_a) {
44184         var color = _a.color, curve = _a.curve;
44185         var ctx = this.canvasElCtx();
44186         var widthDelta = curve.endWidth - curve.startWidth;
44187         var drawSteps = Math.floor(curve.length()) * 2;
44188         ctx.beginPath();
44189         ctx.fillStyle = color;
44190         for (var i = 0; i < drawSteps; i += 1) {
44191         var t = i / drawSteps;
44192         var tt = t * t;
44193         var ttt = tt * t;
44194         var u = 1 - t;
44195         var uu = u * u;
44196         var uuu = uu * u;
44197         var x = uuu * curve.startPoint.x;
44198         x += 3 * uu * t * curve.control1.x;
44199         x += 3 * u * tt * curve.control2.x;
44200         x += ttt * curve.endPoint.x;
44201         var y = uuu * curve.startPoint.y;
44202         y += 3 * uu * t * curve.control1.y;
44203         y += 3 * u * tt * curve.control2.y;
44204         y += ttt * curve.endPoint.y;
44205         var width = curve.startWidth + ttt * widthDelta;
44206         this.drawCurveSegment(x, y, width);
44207         }
44208         ctx.closePath();
44209         ctx.fill();
44210     },
44211     
44212     drawCurveSegment: function (x, y, width) {
44213         var ctx = this.canvasElCtx();
44214         ctx.moveTo(x, y);
44215         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44216         this.is_empty = false;
44217     },
44218     
44219     clear: function()
44220     {
44221         var ctx = this.canvasElCtx();
44222         var canvas = this.canvasEl().dom;
44223         ctx.fillStyle = this.bg_color;
44224         ctx.clearRect(0, 0, canvas.width, canvas.height);
44225         ctx.fillRect(0, 0, canvas.width, canvas.height);
44226         this.curve_data = [];
44227         this.reset();
44228         this.is_empty = true;
44229     },
44230     
44231     fileEl: function()
44232     {
44233         return  this.el.select('input',true).first();
44234     },
44235     
44236     canvasEl: function()
44237     {
44238         return this.el.select('canvas',true).first();
44239     },
44240     
44241     canvasElCtx: function()
44242     {
44243         return this.el.select('canvas',true).first().dom.getContext('2d');
44244     },
44245     
44246     getImage: function(type)
44247     {
44248         if(this.is_empty) {
44249             return false;
44250         }
44251         
44252         // encryption ?
44253         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44254     },
44255     
44256     drawFromImage: function(img_src)
44257     {
44258         var img = new Image();
44259         
44260         img.onload = function(){
44261             this.canvasElCtx().drawImage(img, 0, 0);
44262         }.bind(this);
44263         
44264         img.src = img_src;
44265         
44266         this.is_empty = false;
44267     },
44268     
44269     selectImage: function()
44270     {
44271         this.fileEl().dom.click();
44272     },
44273     
44274     uploadImage: function(e)
44275     {
44276         var reader = new FileReader();
44277         
44278         reader.onload = function(e){
44279             var img = new Image();
44280             img.onload = function(){
44281                 this.reset();
44282                 this.canvasElCtx().drawImage(img, 0, 0);
44283             }.bind(this);
44284             img.src = e.target.result;
44285         }.bind(this);
44286         
44287         reader.readAsDataURL(e.target.files[0]);
44288     },
44289     
44290     // Bezier Point Constructor
44291     Point: (function () {
44292         function Point(x, y, time) {
44293             this.x = x;
44294             this.y = y;
44295             this.time = time || Date.now();
44296         }
44297         Point.prototype.distanceTo = function (start) {
44298             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44299         };
44300         Point.prototype.equals = function (other) {
44301             return this.x === other.x && this.y === other.y && this.time === other.time;
44302         };
44303         Point.prototype.velocityFrom = function (start) {
44304             return this.time !== start.time
44305             ? this.distanceTo(start) / (this.time - start.time)
44306             : 0;
44307         };
44308         return Point;
44309     }()),
44310     
44311     
44312     // Bezier Constructor
44313     Bezier: (function () {
44314         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44315             this.startPoint = startPoint;
44316             this.control2 = control2;
44317             this.control1 = control1;
44318             this.endPoint = endPoint;
44319             this.startWidth = startWidth;
44320             this.endWidth = endWidth;
44321         }
44322         Bezier.fromPoints = function (points, widths, scope) {
44323             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44324             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44325             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44326         };
44327         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44328             var dx1 = s1.x - s2.x;
44329             var dy1 = s1.y - s2.y;
44330             var dx2 = s2.x - s3.x;
44331             var dy2 = s2.y - s3.y;
44332             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44333             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44334             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44335             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44336             var dxm = m1.x - m2.x;
44337             var dym = m1.y - m2.y;
44338             var k = l2 / (l1 + l2);
44339             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44340             var tx = s2.x - cm.x;
44341             var ty = s2.y - cm.y;
44342             return {
44343                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44344                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44345             };
44346         };
44347         Bezier.prototype.length = function () {
44348             var steps = 10;
44349             var length = 0;
44350             var px;
44351             var py;
44352             for (var i = 0; i <= steps; i += 1) {
44353                 var t = i / steps;
44354                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44355                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44356                 if (i > 0) {
44357                     var xdiff = cx - px;
44358                     var ydiff = cy - py;
44359                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44360                 }
44361                 px = cx;
44362                 py = cy;
44363             }
44364             return length;
44365         };
44366         Bezier.prototype.point = function (t, start, c1, c2, end) {
44367             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44368             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44369             + (3.0 * c2 * (1.0 - t) * t * t)
44370             + (end * t * t * t);
44371         };
44372         return Bezier;
44373     }()),
44374     
44375     throttleStroke: function(fn, wait) {
44376       if (wait === void 0) { wait = 250; }
44377       var previous = 0;
44378       var timeout = null;
44379       var result;
44380       var storedContext;
44381       var storedArgs;
44382       var later = function () {
44383           previous = Date.now();
44384           timeout = null;
44385           result = fn.apply(storedContext, storedArgs);
44386           if (!timeout) {
44387               storedContext = null;
44388               storedArgs = [];
44389           }
44390       };
44391       return function wrapper() {
44392           var args = [];
44393           for (var _i = 0; _i < arguments.length; _i++) {
44394               args[_i] = arguments[_i];
44395           }
44396           var now = Date.now();
44397           var remaining = wait - (now - previous);
44398           storedContext = this;
44399           storedArgs = args;
44400           if (remaining <= 0 || remaining > wait) {
44401               if (timeout) {
44402                   clearTimeout(timeout);
44403                   timeout = null;
44404               }
44405               previous = now;
44406               result = fn.apply(storedContext, storedArgs);
44407               if (!timeout) {
44408                   storedContext = null;
44409                   storedArgs = [];
44410               }
44411           }
44412           else if (!timeout) {
44413               timeout = window.setTimeout(later, remaining);
44414           }
44415           return result;
44416       };
44417   }
44418   
44419 });
44420
44421  
44422
44423