5a8472bd949ca5408eb6477eca618cbcbf4bcad6
[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     alignEl : false, // when show is called with an element - this get's stored.
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         this.arrowEl = this.el.select('.arrow',true).first();
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  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         if (!this.el) {
19881             this.render(document.body);
19882         }
19883         
19884         
19885         this.el.removeClass([
19886             'fade','top','bottom', 'left', 'right','in',
19887             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19888         ]);
19889         
19890         if (this.title === false) {
19891             this.headerEl.hide();
19892         }
19893         
19894         // why make it so complicated... - we used to support functional calls for this .. why ?
19895         
19896             
19897         /*
19898         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19899         
19900         // I think  'auto right' - but 
19901         
19902         var autoPlace = autoToken.test(placement);
19903         if (autoPlace) {
19904             placement = placement.replace(autoToken, '') || 'top';
19905         }
19906         */
19907         
19908         
19909         this.el.show();
19910         this.el.dom.style.display='block';
19911         
19912         //this.el.appendTo(on_el);
19913         
19914         var p = this.getPosition();
19915         var box = this.el.getBox();
19916         
19917         
19918         this.el.addClass(placement + ' roo-popover-' + placement);
19919
19920         if (on_el) {
19921             this.updatePosition();
19922              
19923         } else {
19924             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19925             var es = this.el.getSize();
19926             var x = Roo.lib.Dom.getViewWidth()/2;
19927             var y = Roo.lib.Dom.getViewHeight()/2;
19928             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19929             
19930         }
19931
19932         
19933         //var arrow = this.el.select('.arrow',true).first();
19934         //arrow.set(align[2], 
19935         
19936         this.el.addClass('in');
19937         
19938          
19939         
19940         this.hoverState = 'in';
19941         
19942         if (this.modal) {
19943             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19944             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945             this.maskEl.dom.style.display = 'block';
19946             this.maskEl.addClass('show');
19947         }
19948         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19949  
19950         this.fireEvent('show', this);
19951         
19952     },
19953     /**
19954      * fire this manually after loading a grid in the table for example
19955      *
19956      */
19957     updatePosition : function()
19958     {
19959         if (!this.alignEl || !this.alignment) {
19960             return;
19961         }
19962         this.el.alignTo(this.alignEl , this.alignment[0],this.alignment[1]);
19963         
19964         // work out the pointy position.
19965         var p1 = this.alignment[0].split('-').pop().replace('?','');
19966         var xy = this.alignEl.getAnchorXY(p1, false);
19967         xy[0]+=2;xy[1]+=5;
19968         this.arrowEl.setXY(xy);
19969         
19970     },
19971     
19972     hide : function()
19973     {
19974         this.el.setXY([0,0]);
19975         this.el.removeClass('in');
19976         this.el.hide();
19977         this.hoverState = null;
19978         this.maskEl.hide(); // always..
19979         this.fireEvent('hide', this);
19980     }
19981     
19982 });
19983
19984
19985 Roo.apply(Roo.bootstrap.Popover, {
19986
19987     alignment : {
19988         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19989         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19990         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19991         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19992     },
19993     
19994     zIndex : 20001,
19995
19996     clickHander : false,
19997     
19998
19999     onMouseDown : function(e)
20000     {
20001         if (!e.getTarget(".roo-popover")) {
20002             this.hideAll();
20003         }
20004          
20005     },
20006     
20007     popups : [],
20008     
20009     register : function(popup)
20010     {
20011         if (!Roo.bootstrap.Popover.clickHandler) {
20012             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20013         }
20014         // hide other popups.
20015         this.hideAll();
20016         this.popups.push(popup);
20017     },
20018     hideAll : function()
20019     {
20020         this.popups.forEach(function(p) {
20021             p.hide();
20022         });
20023     }
20024
20025 });/*
20026  * - LGPL
20027  *
20028  * Card header - holder for the card header elements.
20029  * 
20030  */
20031
20032 /**
20033  * @class Roo.bootstrap.PopoverNav
20034  * @extends Roo.bootstrap.NavGroup
20035  * Bootstrap Popover header navigation class
20036  * @constructor
20037  * Create a new Popover Header Navigation 
20038  * @param {Object} config The config object
20039  */
20040
20041 Roo.bootstrap.PopoverNav = function(config){
20042     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20043 };
20044
20045 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20046     
20047     
20048     container_method : 'getPopoverHeader' 
20049     
20050      
20051     
20052     
20053    
20054 });
20055
20056  
20057
20058  /*
20059  * - LGPL
20060  *
20061  * Progress
20062  * 
20063  */
20064
20065 /**
20066  * @class Roo.bootstrap.Progress
20067  * @extends Roo.bootstrap.Component
20068  * Bootstrap Progress class
20069  * @cfg {Boolean} striped striped of the progress bar
20070  * @cfg {Boolean} active animated of the progress bar
20071  * 
20072  * 
20073  * @constructor
20074  * Create a new Progress
20075  * @param {Object} config The config object
20076  */
20077
20078 Roo.bootstrap.Progress = function(config){
20079     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20080 };
20081
20082 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20083     
20084     striped : false,
20085     active: false,
20086     
20087     getAutoCreate : function(){
20088         var cfg = {
20089             tag: 'div',
20090             cls: 'progress'
20091         };
20092         
20093         
20094         if(this.striped){
20095             cfg.cls += ' progress-striped';
20096         }
20097       
20098         if(this.active){
20099             cfg.cls += ' active';
20100         }
20101         
20102         
20103         return cfg;
20104     }
20105    
20106 });
20107
20108  
20109
20110  /*
20111  * - LGPL
20112  *
20113  * ProgressBar
20114  * 
20115  */
20116
20117 /**
20118  * @class Roo.bootstrap.ProgressBar
20119  * @extends Roo.bootstrap.Component
20120  * Bootstrap ProgressBar class
20121  * @cfg {Number} aria_valuenow aria-value now
20122  * @cfg {Number} aria_valuemin aria-value min
20123  * @cfg {Number} aria_valuemax aria-value max
20124  * @cfg {String} label label for the progress bar
20125  * @cfg {String} panel (success | info | warning | danger )
20126  * @cfg {String} role role of the progress bar
20127  * @cfg {String} sr_only text
20128  * 
20129  * 
20130  * @constructor
20131  * Create a new ProgressBar
20132  * @param {Object} config The config object
20133  */
20134
20135 Roo.bootstrap.ProgressBar = function(config){
20136     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20137 };
20138
20139 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20140     
20141     aria_valuenow : 0,
20142     aria_valuemin : 0,
20143     aria_valuemax : 100,
20144     label : false,
20145     panel : false,
20146     role : false,
20147     sr_only: false,
20148     
20149     getAutoCreate : function()
20150     {
20151         
20152         var cfg = {
20153             tag: 'div',
20154             cls: 'progress-bar',
20155             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20156         };
20157         
20158         if(this.sr_only){
20159             cfg.cn = {
20160                 tag: 'span',
20161                 cls: 'sr-only',
20162                 html: this.sr_only
20163             }
20164         }
20165         
20166         if(this.role){
20167             cfg.role = this.role;
20168         }
20169         
20170         if(this.aria_valuenow){
20171             cfg['aria-valuenow'] = this.aria_valuenow;
20172         }
20173         
20174         if(this.aria_valuemin){
20175             cfg['aria-valuemin'] = this.aria_valuemin;
20176         }
20177         
20178         if(this.aria_valuemax){
20179             cfg['aria-valuemax'] = this.aria_valuemax;
20180         }
20181         
20182         if(this.label && !this.sr_only){
20183             cfg.html = this.label;
20184         }
20185         
20186         if(this.panel){
20187             cfg.cls += ' progress-bar-' + this.panel;
20188         }
20189         
20190         return cfg;
20191     },
20192     
20193     update : function(aria_valuenow)
20194     {
20195         this.aria_valuenow = aria_valuenow;
20196         
20197         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20198     }
20199    
20200 });
20201
20202  
20203
20204  /*
20205  * - LGPL
20206  *
20207  * column
20208  * 
20209  */
20210
20211 /**
20212  * @class Roo.bootstrap.TabGroup
20213  * @extends Roo.bootstrap.Column
20214  * Bootstrap Column class
20215  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20216  * @cfg {Boolean} carousel true to make the group behave like a carousel
20217  * @cfg {Boolean} bullets show bullets for the panels
20218  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20219  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20220  * @cfg {Boolean} showarrow (true|false) show arrow default true
20221  * 
20222  * @constructor
20223  * Create a new TabGroup
20224  * @param {Object} config The config object
20225  */
20226
20227 Roo.bootstrap.TabGroup = function(config){
20228     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20229     if (!this.navId) {
20230         this.navId = Roo.id();
20231     }
20232     this.tabs = [];
20233     Roo.bootstrap.TabGroup.register(this);
20234     
20235 };
20236
20237 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20238     
20239     carousel : false,
20240     transition : false,
20241     bullets : 0,
20242     timer : 0,
20243     autoslide : false,
20244     slideFn : false,
20245     slideOnTouch : false,
20246     showarrow : true,
20247     
20248     getAutoCreate : function()
20249     {
20250         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20251         
20252         cfg.cls += ' tab-content';
20253         
20254         if (this.carousel) {
20255             cfg.cls += ' carousel slide';
20256             
20257             cfg.cn = [{
20258                cls : 'carousel-inner',
20259                cn : []
20260             }];
20261         
20262             if(this.bullets  && !Roo.isTouch){
20263                 
20264                 var bullets = {
20265                     cls : 'carousel-bullets',
20266                     cn : []
20267                 };
20268                
20269                 if(this.bullets_cls){
20270                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20271                 }
20272                 
20273                 bullets.cn.push({
20274                     cls : 'clear'
20275                 });
20276                 
20277                 cfg.cn[0].cn.push(bullets);
20278             }
20279             
20280             if(this.showarrow){
20281                 cfg.cn[0].cn.push({
20282                     tag : 'div',
20283                     class : 'carousel-arrow',
20284                     cn : [
20285                         {
20286                             tag : 'div',
20287                             class : 'carousel-prev',
20288                             cn : [
20289                                 {
20290                                     tag : 'i',
20291                                     class : 'fa fa-chevron-left'
20292                                 }
20293                             ]
20294                         },
20295                         {
20296                             tag : 'div',
20297                             class : 'carousel-next',
20298                             cn : [
20299                                 {
20300                                     tag : 'i',
20301                                     class : 'fa fa-chevron-right'
20302                                 }
20303                             ]
20304                         }
20305                     ]
20306                 });
20307             }
20308             
20309         }
20310         
20311         return cfg;
20312     },
20313     
20314     initEvents:  function()
20315     {
20316 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20317 //            this.el.on("touchstart", this.onTouchStart, this);
20318 //        }
20319         
20320         if(this.autoslide){
20321             var _this = this;
20322             
20323             this.slideFn = window.setInterval(function() {
20324                 _this.showPanelNext();
20325             }, this.timer);
20326         }
20327         
20328         if(this.showarrow){
20329             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20330             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20331         }
20332         
20333         
20334     },
20335     
20336 //    onTouchStart : function(e, el, o)
20337 //    {
20338 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20339 //            return;
20340 //        }
20341 //        
20342 //        this.showPanelNext();
20343 //    },
20344     
20345     
20346     getChildContainer : function()
20347     {
20348         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20349     },
20350     
20351     /**
20352     * register a Navigation item
20353     * @param {Roo.bootstrap.NavItem} the navitem to add
20354     */
20355     register : function(item)
20356     {
20357         this.tabs.push( item);
20358         item.navId = this.navId; // not really needed..
20359         this.addBullet();
20360     
20361     },
20362     
20363     getActivePanel : function()
20364     {
20365         var r = false;
20366         Roo.each(this.tabs, function(t) {
20367             if (t.active) {
20368                 r = t;
20369                 return false;
20370             }
20371             return null;
20372         });
20373         return r;
20374         
20375     },
20376     getPanelByName : function(n)
20377     {
20378         var r = false;
20379         Roo.each(this.tabs, function(t) {
20380             if (t.tabId == n) {
20381                 r = t;
20382                 return false;
20383             }
20384             return null;
20385         });
20386         return r;
20387     },
20388     indexOfPanel : function(p)
20389     {
20390         var r = false;
20391         Roo.each(this.tabs, function(t,i) {
20392             if (t.tabId == p.tabId) {
20393                 r = i;
20394                 return false;
20395             }
20396             return null;
20397         });
20398         return r;
20399     },
20400     /**
20401      * show a specific panel
20402      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20403      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20404      */
20405     showPanel : function (pan)
20406     {
20407         if(this.transition || typeof(pan) == 'undefined'){
20408             Roo.log("waiting for the transitionend");
20409             return false;
20410         }
20411         
20412         if (typeof(pan) == 'number') {
20413             pan = this.tabs[pan];
20414         }
20415         
20416         if (typeof(pan) == 'string') {
20417             pan = this.getPanelByName(pan);
20418         }
20419         
20420         var cur = this.getActivePanel();
20421         
20422         if(!pan || !cur){
20423             Roo.log('pan or acitve pan is undefined');
20424             return false;
20425         }
20426         
20427         if (pan.tabId == this.getActivePanel().tabId) {
20428             return true;
20429         }
20430         
20431         if (false === cur.fireEvent('beforedeactivate')) {
20432             return false;
20433         }
20434         
20435         if(this.bullets > 0 && !Roo.isTouch){
20436             this.setActiveBullet(this.indexOfPanel(pan));
20437         }
20438         
20439         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20440             
20441             //class="carousel-item carousel-item-next carousel-item-left"
20442             
20443             this.transition = true;
20444             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20445             var lr = dir == 'next' ? 'left' : 'right';
20446             pan.el.addClass(dir); // or prev
20447             pan.el.addClass('carousel-item-' + dir); // or prev
20448             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20449             cur.el.addClass(lr); // or right
20450             pan.el.addClass(lr);
20451             cur.el.addClass('carousel-item-' +lr); // or right
20452             pan.el.addClass('carousel-item-' +lr);
20453             
20454             
20455             var _this = this;
20456             cur.el.on('transitionend', function() {
20457                 Roo.log("trans end?");
20458                 
20459                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20460                 pan.setActive(true);
20461                 
20462                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20463                 cur.setActive(false);
20464                 
20465                 _this.transition = false;
20466                 
20467             }, this, { single:  true } );
20468             
20469             return true;
20470         }
20471         
20472         cur.setActive(false);
20473         pan.setActive(true);
20474         
20475         return true;
20476         
20477     },
20478     showPanelNext : function()
20479     {
20480         var i = this.indexOfPanel(this.getActivePanel());
20481         
20482         if (i >= this.tabs.length - 1 && !this.autoslide) {
20483             return;
20484         }
20485         
20486         if (i >= this.tabs.length - 1 && this.autoslide) {
20487             i = -1;
20488         }
20489         
20490         this.showPanel(this.tabs[i+1]);
20491     },
20492     
20493     showPanelPrev : function()
20494     {
20495         var i = this.indexOfPanel(this.getActivePanel());
20496         
20497         if (i  < 1 && !this.autoslide) {
20498             return;
20499         }
20500         
20501         if (i < 1 && this.autoslide) {
20502             i = this.tabs.length;
20503         }
20504         
20505         this.showPanel(this.tabs[i-1]);
20506     },
20507     
20508     
20509     addBullet: function()
20510     {
20511         if(!this.bullets || Roo.isTouch){
20512             return;
20513         }
20514         var ctr = this.el.select('.carousel-bullets',true).first();
20515         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20516         var bullet = ctr.createChild({
20517             cls : 'bullet bullet-' + i
20518         },ctr.dom.lastChild);
20519         
20520         
20521         var _this = this;
20522         
20523         bullet.on('click', (function(e, el, o, ii, t){
20524
20525             e.preventDefault();
20526
20527             this.showPanel(ii);
20528
20529             if(this.autoslide && this.slideFn){
20530                 clearInterval(this.slideFn);
20531                 this.slideFn = window.setInterval(function() {
20532                     _this.showPanelNext();
20533                 }, this.timer);
20534             }
20535
20536         }).createDelegate(this, [i, bullet], true));
20537                 
20538         
20539     },
20540      
20541     setActiveBullet : function(i)
20542     {
20543         if(Roo.isTouch){
20544             return;
20545         }
20546         
20547         Roo.each(this.el.select('.bullet', true).elements, function(el){
20548             el.removeClass('selected');
20549         });
20550
20551         var bullet = this.el.select('.bullet-' + i, true).first();
20552         
20553         if(!bullet){
20554             return;
20555         }
20556         
20557         bullet.addClass('selected');
20558     }
20559     
20560     
20561   
20562 });
20563
20564  
20565
20566  
20567  
20568 Roo.apply(Roo.bootstrap.TabGroup, {
20569     
20570     groups: {},
20571      /**
20572     * register a Navigation Group
20573     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20574     */
20575     register : function(navgrp)
20576     {
20577         this.groups[navgrp.navId] = navgrp;
20578         
20579     },
20580     /**
20581     * fetch a Navigation Group based on the navigation ID
20582     * if one does not exist , it will get created.
20583     * @param {string} the navgroup to add
20584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20585     */
20586     get: function(navId) {
20587         if (typeof(this.groups[navId]) == 'undefined') {
20588             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20589         }
20590         return this.groups[navId] ;
20591     }
20592     
20593     
20594     
20595 });
20596
20597  /*
20598  * - LGPL
20599  *
20600  * TabPanel
20601  * 
20602  */
20603
20604 /**
20605  * @class Roo.bootstrap.TabPanel
20606  * @extends Roo.bootstrap.Component
20607  * Bootstrap TabPanel class
20608  * @cfg {Boolean} active panel active
20609  * @cfg {String} html panel content
20610  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20611  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20612  * @cfg {String} href click to link..
20613  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20614  * 
20615  * 
20616  * @constructor
20617  * Create a new TabPanel
20618  * @param {Object} config The config object
20619  */
20620
20621 Roo.bootstrap.TabPanel = function(config){
20622     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20623     this.addEvents({
20624         /**
20625              * @event changed
20626              * Fires when the active status changes
20627              * @param {Roo.bootstrap.TabPanel} this
20628              * @param {Boolean} state the new state
20629             
20630          */
20631         'changed': true,
20632         /**
20633              * @event beforedeactivate
20634              * Fires before a tab is de-activated - can be used to do validation on a form.
20635              * @param {Roo.bootstrap.TabPanel} this
20636              * @return {Boolean} false if there is an error
20637             
20638          */
20639         'beforedeactivate': true
20640      });
20641     
20642     this.tabId = this.tabId || Roo.id();
20643   
20644 };
20645
20646 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20647     
20648     active: false,
20649     html: false,
20650     tabId: false,
20651     navId : false,
20652     href : '',
20653     touchSlide : false,
20654     getAutoCreate : function(){
20655         
20656         
20657         var cfg = {
20658             tag: 'div',
20659             // item is needed for carousel - not sure if it has any effect otherwise
20660             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20661             html: this.html || ''
20662         };
20663         
20664         if(this.active){
20665             cfg.cls += ' active';
20666         }
20667         
20668         if(this.tabId){
20669             cfg.tabId = this.tabId;
20670         }
20671         
20672         
20673         
20674         return cfg;
20675     },
20676     
20677     initEvents:  function()
20678     {
20679         var p = this.parent();
20680         
20681         this.navId = this.navId || p.navId;
20682         
20683         if (typeof(this.navId) != 'undefined') {
20684             // not really needed.. but just in case.. parent should be a NavGroup.
20685             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20686             
20687             tg.register(this);
20688             
20689             var i = tg.tabs.length - 1;
20690             
20691             if(this.active && tg.bullets > 0 && i < tg.bullets){
20692                 tg.setActiveBullet(i);
20693             }
20694         }
20695         
20696         this.el.on('click', this.onClick, this);
20697         
20698         if(Roo.isTouch && this.touchSlide){
20699             this.el.on("touchstart", this.onTouchStart, this);
20700             this.el.on("touchmove", this.onTouchMove, this);
20701             this.el.on("touchend", this.onTouchEnd, this);
20702         }
20703         
20704     },
20705     
20706     onRender : function(ct, position)
20707     {
20708         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20709     },
20710     
20711     setActive : function(state)
20712     {
20713         Roo.log("panel - set active " + this.tabId + "=" + state);
20714         
20715         this.active = state;
20716         if (!state) {
20717             this.el.removeClass('active');
20718             
20719         } else  if (!this.el.hasClass('active')) {
20720             this.el.addClass('active');
20721         }
20722         
20723         this.fireEvent('changed', this, state);
20724     },
20725     
20726     onClick : function(e)
20727     {
20728         e.preventDefault();
20729         
20730         if(!this.href.length){
20731             return;
20732         }
20733         
20734         window.location.href = this.href;
20735     },
20736     
20737     startX : 0,
20738     startY : 0,
20739     endX : 0,
20740     endY : 0,
20741     swiping : false,
20742     
20743     onTouchStart : function(e)
20744     {
20745         this.swiping = false;
20746         
20747         this.startX = e.browserEvent.touches[0].clientX;
20748         this.startY = e.browserEvent.touches[0].clientY;
20749     },
20750     
20751     onTouchMove : function(e)
20752     {
20753         this.swiping = true;
20754         
20755         this.endX = e.browserEvent.touches[0].clientX;
20756         this.endY = e.browserEvent.touches[0].clientY;
20757     },
20758     
20759     onTouchEnd : function(e)
20760     {
20761         if(!this.swiping){
20762             this.onClick(e);
20763             return;
20764         }
20765         
20766         var tabGroup = this.parent();
20767         
20768         if(this.endX > this.startX){ // swiping right
20769             tabGroup.showPanelPrev();
20770             return;
20771         }
20772         
20773         if(this.startX > this.endX){ // swiping left
20774             tabGroup.showPanelNext();
20775             return;
20776         }
20777     }
20778     
20779     
20780 });
20781  
20782
20783  
20784
20785  /*
20786  * - LGPL
20787  *
20788  * DateField
20789  * 
20790  */
20791
20792 /**
20793  * @class Roo.bootstrap.DateField
20794  * @extends Roo.bootstrap.Input
20795  * Bootstrap DateField class
20796  * @cfg {Number} weekStart default 0
20797  * @cfg {String} viewMode default empty, (months|years)
20798  * @cfg {String} minViewMode default empty, (months|years)
20799  * @cfg {Number} startDate default -Infinity
20800  * @cfg {Number} endDate default Infinity
20801  * @cfg {Boolean} todayHighlight default false
20802  * @cfg {Boolean} todayBtn default false
20803  * @cfg {Boolean} calendarWeeks default false
20804  * @cfg {Object} daysOfWeekDisabled default empty
20805  * @cfg {Boolean} singleMode default false (true | false)
20806  * 
20807  * @cfg {Boolean} keyboardNavigation default true
20808  * @cfg {String} language default en
20809  * 
20810  * @constructor
20811  * Create a new DateField
20812  * @param {Object} config The config object
20813  */
20814
20815 Roo.bootstrap.DateField = function(config){
20816     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20817      this.addEvents({
20818             /**
20819              * @event show
20820              * Fires when this field show.
20821              * @param {Roo.bootstrap.DateField} this
20822              * @param {Mixed} date The date value
20823              */
20824             show : true,
20825             /**
20826              * @event show
20827              * Fires when this field hide.
20828              * @param {Roo.bootstrap.DateField} this
20829              * @param {Mixed} date The date value
20830              */
20831             hide : true,
20832             /**
20833              * @event select
20834              * Fires when select a date.
20835              * @param {Roo.bootstrap.DateField} this
20836              * @param {Mixed} date The date value
20837              */
20838             select : true,
20839             /**
20840              * @event beforeselect
20841              * Fires when before select a date.
20842              * @param {Roo.bootstrap.DateField} this
20843              * @param {Mixed} date The date value
20844              */
20845             beforeselect : true
20846         });
20847 };
20848
20849 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20850     
20851     /**
20852      * @cfg {String} format
20853      * The default date format string which can be overriden for localization support.  The format must be
20854      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20855      */
20856     format : "m/d/y",
20857     /**
20858      * @cfg {String} altFormats
20859      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20860      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20861      */
20862     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20863     
20864     weekStart : 0,
20865     
20866     viewMode : '',
20867     
20868     minViewMode : '',
20869     
20870     todayHighlight : false,
20871     
20872     todayBtn: false,
20873     
20874     language: 'en',
20875     
20876     keyboardNavigation: true,
20877     
20878     calendarWeeks: false,
20879     
20880     startDate: -Infinity,
20881     
20882     endDate: Infinity,
20883     
20884     daysOfWeekDisabled: [],
20885     
20886     _events: [],
20887     
20888     singleMode : false,
20889     
20890     UTCDate: function()
20891     {
20892         return new Date(Date.UTC.apply(Date, arguments));
20893     },
20894     
20895     UTCToday: function()
20896     {
20897         var today = new Date();
20898         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20899     },
20900     
20901     getDate: function() {
20902             var d = this.getUTCDate();
20903             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20904     },
20905     
20906     getUTCDate: function() {
20907             return this.date;
20908     },
20909     
20910     setDate: function(d) {
20911             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20912     },
20913     
20914     setUTCDate: function(d) {
20915             this.date = d;
20916             this.setValue(this.formatDate(this.date));
20917     },
20918         
20919     onRender: function(ct, position)
20920     {
20921         
20922         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20923         
20924         this.language = this.language || 'en';
20925         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20926         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20927         
20928         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20929         this.format = this.format || 'm/d/y';
20930         this.isInline = false;
20931         this.isInput = true;
20932         this.component = this.el.select('.add-on', true).first() || false;
20933         this.component = (this.component && this.component.length === 0) ? false : this.component;
20934         this.hasInput = this.component && this.inputEl().length;
20935         
20936         if (typeof(this.minViewMode === 'string')) {
20937             switch (this.minViewMode) {
20938                 case 'months':
20939                     this.minViewMode = 1;
20940                     break;
20941                 case 'years':
20942                     this.minViewMode = 2;
20943                     break;
20944                 default:
20945                     this.minViewMode = 0;
20946                     break;
20947             }
20948         }
20949         
20950         if (typeof(this.viewMode === 'string')) {
20951             switch (this.viewMode) {
20952                 case 'months':
20953                     this.viewMode = 1;
20954                     break;
20955                 case 'years':
20956                     this.viewMode = 2;
20957                     break;
20958                 default:
20959                     this.viewMode = 0;
20960                     break;
20961             }
20962         }
20963                 
20964         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20965         
20966 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20967         
20968         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20969         
20970         this.picker().on('mousedown', this.onMousedown, this);
20971         this.picker().on('click', this.onClick, this);
20972         
20973         this.picker().addClass('datepicker-dropdown');
20974         
20975         this.startViewMode = this.viewMode;
20976         
20977         if(this.singleMode){
20978             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20979                 v.setVisibilityMode(Roo.Element.DISPLAY);
20980                 v.hide();
20981             });
20982             
20983             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20984                 v.setStyle('width', '189px');
20985             });
20986         }
20987         
20988         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20989             if(!this.calendarWeeks){
20990                 v.remove();
20991                 return;
20992             }
20993             
20994             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20995             v.attr('colspan', function(i, val){
20996                 return parseInt(val) + 1;
20997             });
20998         });
20999                         
21000         
21001         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21002         
21003         this.setStartDate(this.startDate);
21004         this.setEndDate(this.endDate);
21005         
21006         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21007         
21008         this.fillDow();
21009         this.fillMonths();
21010         this.update();
21011         this.showMode();
21012         
21013         if(this.isInline) {
21014             this.showPopup();
21015         }
21016     },
21017     
21018     picker : function()
21019     {
21020         return this.pickerEl;
21021 //        return this.el.select('.datepicker', true).first();
21022     },
21023     
21024     fillDow: function()
21025     {
21026         var dowCnt = this.weekStart;
21027         
21028         var dow = {
21029             tag: 'tr',
21030             cn: [
21031                 
21032             ]
21033         };
21034         
21035         if(this.calendarWeeks){
21036             dow.cn.push({
21037                 tag: 'th',
21038                 cls: 'cw',
21039                 html: '&nbsp;'
21040             })
21041         }
21042         
21043         while (dowCnt < this.weekStart + 7) {
21044             dow.cn.push({
21045                 tag: 'th',
21046                 cls: 'dow',
21047                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21048             });
21049         }
21050         
21051         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21052     },
21053     
21054     fillMonths: function()
21055     {    
21056         var i = 0;
21057         var months = this.picker().select('>.datepicker-months td', true).first();
21058         
21059         months.dom.innerHTML = '';
21060         
21061         while (i < 12) {
21062             var month = {
21063                 tag: 'span',
21064                 cls: 'month',
21065                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21066             };
21067             
21068             months.createChild(month);
21069         }
21070         
21071     },
21072     
21073     update: function()
21074     {
21075         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;
21076         
21077         if (this.date < this.startDate) {
21078             this.viewDate = new Date(this.startDate);
21079         } else if (this.date > this.endDate) {
21080             this.viewDate = new Date(this.endDate);
21081         } else {
21082             this.viewDate = new Date(this.date);
21083         }
21084         
21085         this.fill();
21086     },
21087     
21088     fill: function() 
21089     {
21090         var d = new Date(this.viewDate),
21091                 year = d.getUTCFullYear(),
21092                 month = d.getUTCMonth(),
21093                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21094                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21095                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21096                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21097                 currentDate = this.date && this.date.valueOf(),
21098                 today = this.UTCToday();
21099         
21100         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21101         
21102 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21103         
21104 //        this.picker.select('>tfoot th.today').
21105 //                                              .text(dates[this.language].today)
21106 //                                              .toggle(this.todayBtn !== false);
21107     
21108         this.updateNavArrows();
21109         this.fillMonths();
21110                                                 
21111         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21112         
21113         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21114          
21115         prevMonth.setUTCDate(day);
21116         
21117         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21118         
21119         var nextMonth = new Date(prevMonth);
21120         
21121         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21122         
21123         nextMonth = nextMonth.valueOf();
21124         
21125         var fillMonths = false;
21126         
21127         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21128         
21129         while(prevMonth.valueOf() <= nextMonth) {
21130             var clsName = '';
21131             
21132             if (prevMonth.getUTCDay() === this.weekStart) {
21133                 if(fillMonths){
21134                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21135                 }
21136                     
21137                 fillMonths = {
21138                     tag: 'tr',
21139                     cn: []
21140                 };
21141                 
21142                 if(this.calendarWeeks){
21143                     // ISO 8601: First week contains first thursday.
21144                     // ISO also states week starts on Monday, but we can be more abstract here.
21145                     var
21146                     // Start of current week: based on weekstart/current date
21147                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21148                     // Thursday of this week
21149                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21150                     // First Thursday of year, year from thursday
21151                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21152                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21153                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21154                     
21155                     fillMonths.cn.push({
21156                         tag: 'td',
21157                         cls: 'cw',
21158                         html: calWeek
21159                     });
21160                 }
21161             }
21162             
21163             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21164                 clsName += ' old';
21165             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21166                 clsName += ' new';
21167             }
21168             if (this.todayHighlight &&
21169                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21170                 prevMonth.getUTCMonth() == today.getMonth() &&
21171                 prevMonth.getUTCDate() == today.getDate()) {
21172                 clsName += ' today';
21173             }
21174             
21175             if (currentDate && prevMonth.valueOf() === currentDate) {
21176                 clsName += ' active';
21177             }
21178             
21179             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21180                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21181                     clsName += ' disabled';
21182             }
21183             
21184             fillMonths.cn.push({
21185                 tag: 'td',
21186                 cls: 'day ' + clsName,
21187                 html: prevMonth.getDate()
21188             });
21189             
21190             prevMonth.setDate(prevMonth.getDate()+1);
21191         }
21192           
21193         var currentYear = this.date && this.date.getUTCFullYear();
21194         var currentMonth = this.date && this.date.getUTCMonth();
21195         
21196         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21197         
21198         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21199             v.removeClass('active');
21200             
21201             if(currentYear === year && k === currentMonth){
21202                 v.addClass('active');
21203             }
21204             
21205             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21206                 v.addClass('disabled');
21207             }
21208             
21209         });
21210         
21211         
21212         year = parseInt(year/10, 10) * 10;
21213         
21214         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21215         
21216         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21217         
21218         year -= 1;
21219         for (var i = -1; i < 11; i++) {
21220             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21221                 tag: 'span',
21222                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21223                 html: year
21224             });
21225             
21226             year += 1;
21227         }
21228     },
21229     
21230     showMode: function(dir) 
21231     {
21232         if (dir) {
21233             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21234         }
21235         
21236         Roo.each(this.picker().select('>div',true).elements, function(v){
21237             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21238             v.hide();
21239         });
21240         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21241     },
21242     
21243     place: function()
21244     {
21245         if(this.isInline) {
21246             return;
21247         }
21248         
21249         this.picker().removeClass(['bottom', 'top']);
21250         
21251         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21252             /*
21253              * place to the top of element!
21254              *
21255              */
21256             
21257             this.picker().addClass('top');
21258             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21259             
21260             return;
21261         }
21262         
21263         this.picker().addClass('bottom');
21264         
21265         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21266     },
21267     
21268     parseDate : function(value)
21269     {
21270         if(!value || value instanceof Date){
21271             return value;
21272         }
21273         var v = Date.parseDate(value, this.format);
21274         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21275             v = Date.parseDate(value, 'Y-m-d');
21276         }
21277         if(!v && this.altFormats){
21278             if(!this.altFormatsArray){
21279                 this.altFormatsArray = this.altFormats.split("|");
21280             }
21281             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21282                 v = Date.parseDate(value, this.altFormatsArray[i]);
21283             }
21284         }
21285         return v;
21286     },
21287     
21288     formatDate : function(date, fmt)
21289     {   
21290         return (!date || !(date instanceof Date)) ?
21291         date : date.dateFormat(fmt || this.format);
21292     },
21293     
21294     onFocus : function()
21295     {
21296         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21297         this.showPopup();
21298     },
21299     
21300     onBlur : function()
21301     {
21302         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21303         
21304         var d = this.inputEl().getValue();
21305         
21306         this.setValue(d);
21307                 
21308         this.hidePopup();
21309     },
21310     
21311     showPopup : function()
21312     {
21313         this.picker().show();
21314         this.update();
21315         this.place();
21316         
21317         this.fireEvent('showpopup', this, this.date);
21318     },
21319     
21320     hidePopup : function()
21321     {
21322         if(this.isInline) {
21323             return;
21324         }
21325         this.picker().hide();
21326         this.viewMode = this.startViewMode;
21327         this.showMode();
21328         
21329         this.fireEvent('hidepopup', this, this.date);
21330         
21331     },
21332     
21333     onMousedown: function(e)
21334     {
21335         e.stopPropagation();
21336         e.preventDefault();
21337     },
21338     
21339     keyup: function(e)
21340     {
21341         Roo.bootstrap.DateField.superclass.keyup.call(this);
21342         this.update();
21343     },
21344
21345     setValue: function(v)
21346     {
21347         if(this.fireEvent('beforeselect', this, v) !== false){
21348             var d = new Date(this.parseDate(v) ).clearTime();
21349         
21350             if(isNaN(d.getTime())){
21351                 this.date = this.viewDate = '';
21352                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21353                 return;
21354             }
21355
21356             v = this.formatDate(d);
21357
21358             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21359
21360             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21361
21362             this.update();
21363
21364             this.fireEvent('select', this, this.date);
21365         }
21366     },
21367     
21368     getValue: function()
21369     {
21370         return this.formatDate(this.date);
21371     },
21372     
21373     fireKey: function(e)
21374     {
21375         if (!this.picker().isVisible()){
21376             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21377                 this.showPopup();
21378             }
21379             return;
21380         }
21381         
21382         var dateChanged = false,
21383         dir, day, month,
21384         newDate, newViewDate;
21385         
21386         switch(e.keyCode){
21387             case 27: // escape
21388                 this.hidePopup();
21389                 e.preventDefault();
21390                 break;
21391             case 37: // left
21392             case 39: // right
21393                 if (!this.keyboardNavigation) {
21394                     break;
21395                 }
21396                 dir = e.keyCode == 37 ? -1 : 1;
21397                 
21398                 if (e.ctrlKey){
21399                     newDate = this.moveYear(this.date, dir);
21400                     newViewDate = this.moveYear(this.viewDate, dir);
21401                 } else if (e.shiftKey){
21402                     newDate = this.moveMonth(this.date, dir);
21403                     newViewDate = this.moveMonth(this.viewDate, dir);
21404                 } else {
21405                     newDate = new Date(this.date);
21406                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21407                     newViewDate = new Date(this.viewDate);
21408                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21409                 }
21410                 if (this.dateWithinRange(newDate)){
21411                     this.date = newDate;
21412                     this.viewDate = newViewDate;
21413                     this.setValue(this.formatDate(this.date));
21414 //                    this.update();
21415                     e.preventDefault();
21416                     dateChanged = true;
21417                 }
21418                 break;
21419             case 38: // up
21420             case 40: // down
21421                 if (!this.keyboardNavigation) {
21422                     break;
21423                 }
21424                 dir = e.keyCode == 38 ? -1 : 1;
21425                 if (e.ctrlKey){
21426                     newDate = this.moveYear(this.date, dir);
21427                     newViewDate = this.moveYear(this.viewDate, dir);
21428                 } else if (e.shiftKey){
21429                     newDate = this.moveMonth(this.date, dir);
21430                     newViewDate = this.moveMonth(this.viewDate, dir);
21431                 } else {
21432                     newDate = new Date(this.date);
21433                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21434                     newViewDate = new Date(this.viewDate);
21435                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21436                 }
21437                 if (this.dateWithinRange(newDate)){
21438                     this.date = newDate;
21439                     this.viewDate = newViewDate;
21440                     this.setValue(this.formatDate(this.date));
21441 //                    this.update();
21442                     e.preventDefault();
21443                     dateChanged = true;
21444                 }
21445                 break;
21446             case 13: // enter
21447                 this.setValue(this.formatDate(this.date));
21448                 this.hidePopup();
21449                 e.preventDefault();
21450                 break;
21451             case 9: // tab
21452                 this.setValue(this.formatDate(this.date));
21453                 this.hidePopup();
21454                 break;
21455             case 16: // shift
21456             case 17: // ctrl
21457             case 18: // alt
21458                 break;
21459             default :
21460                 this.hidePopup();
21461                 
21462         }
21463     },
21464     
21465     
21466     onClick: function(e) 
21467     {
21468         e.stopPropagation();
21469         e.preventDefault();
21470         
21471         var target = e.getTarget();
21472         
21473         if(target.nodeName.toLowerCase() === 'i'){
21474             target = Roo.get(target).dom.parentNode;
21475         }
21476         
21477         var nodeName = target.nodeName;
21478         var className = target.className;
21479         var html = target.innerHTML;
21480         //Roo.log(nodeName);
21481         
21482         switch(nodeName.toLowerCase()) {
21483             case 'th':
21484                 switch(className) {
21485                     case 'switch':
21486                         this.showMode(1);
21487                         break;
21488                     case 'prev':
21489                     case 'next':
21490                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21491                         switch(this.viewMode){
21492                                 case 0:
21493                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21494                                         break;
21495                                 case 1:
21496                                 case 2:
21497                                         this.viewDate = this.moveYear(this.viewDate, dir);
21498                                         break;
21499                         }
21500                         this.fill();
21501                         break;
21502                     case 'today':
21503                         var date = new Date();
21504                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21505 //                        this.fill()
21506                         this.setValue(this.formatDate(this.date));
21507                         
21508                         this.hidePopup();
21509                         break;
21510                 }
21511                 break;
21512             case 'span':
21513                 if (className.indexOf('disabled') < 0) {
21514                     this.viewDate.setUTCDate(1);
21515                     if (className.indexOf('month') > -1) {
21516                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21517                     } else {
21518                         var year = parseInt(html, 10) || 0;
21519                         this.viewDate.setUTCFullYear(year);
21520                         
21521                     }
21522                     
21523                     if(this.singleMode){
21524                         this.setValue(this.formatDate(this.viewDate));
21525                         this.hidePopup();
21526                         return;
21527                     }
21528                     
21529                     this.showMode(-1);
21530                     this.fill();
21531                 }
21532                 break;
21533                 
21534             case 'td':
21535                 //Roo.log(className);
21536                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21537                     var day = parseInt(html, 10) || 1;
21538                     var year = this.viewDate.getUTCFullYear(),
21539                         month = this.viewDate.getUTCMonth();
21540
21541                     if (className.indexOf('old') > -1) {
21542                         if(month === 0 ){
21543                             month = 11;
21544                             year -= 1;
21545                         }else{
21546                             month -= 1;
21547                         }
21548                     } else if (className.indexOf('new') > -1) {
21549                         if (month == 11) {
21550                             month = 0;
21551                             year += 1;
21552                         } else {
21553                             month += 1;
21554                         }
21555                     }
21556                     //Roo.log([year,month,day]);
21557                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21558                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21559 //                    this.fill();
21560                     //Roo.log(this.formatDate(this.date));
21561                     this.setValue(this.formatDate(this.date));
21562                     this.hidePopup();
21563                 }
21564                 break;
21565         }
21566     },
21567     
21568     setStartDate: function(startDate)
21569     {
21570         this.startDate = startDate || -Infinity;
21571         if (this.startDate !== -Infinity) {
21572             this.startDate = this.parseDate(this.startDate);
21573         }
21574         this.update();
21575         this.updateNavArrows();
21576     },
21577
21578     setEndDate: function(endDate)
21579     {
21580         this.endDate = endDate || Infinity;
21581         if (this.endDate !== Infinity) {
21582             this.endDate = this.parseDate(this.endDate);
21583         }
21584         this.update();
21585         this.updateNavArrows();
21586     },
21587     
21588     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21589     {
21590         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21591         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21592             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21593         }
21594         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21595             return parseInt(d, 10);
21596         });
21597         this.update();
21598         this.updateNavArrows();
21599     },
21600     
21601     updateNavArrows: function() 
21602     {
21603         if(this.singleMode){
21604             return;
21605         }
21606         
21607         var d = new Date(this.viewDate),
21608         year = d.getUTCFullYear(),
21609         month = d.getUTCMonth();
21610         
21611         Roo.each(this.picker().select('.prev', true).elements, function(v){
21612             v.show();
21613             switch (this.viewMode) {
21614                 case 0:
21615
21616                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21617                         v.hide();
21618                     }
21619                     break;
21620                 case 1:
21621                 case 2:
21622                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21623                         v.hide();
21624                     }
21625                     break;
21626             }
21627         });
21628         
21629         Roo.each(this.picker().select('.next', true).elements, function(v){
21630             v.show();
21631             switch (this.viewMode) {
21632                 case 0:
21633
21634                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21635                         v.hide();
21636                     }
21637                     break;
21638                 case 1:
21639                 case 2:
21640                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21641                         v.hide();
21642                     }
21643                     break;
21644             }
21645         })
21646     },
21647     
21648     moveMonth: function(date, dir)
21649     {
21650         if (!dir) {
21651             return date;
21652         }
21653         var new_date = new Date(date.valueOf()),
21654         day = new_date.getUTCDate(),
21655         month = new_date.getUTCMonth(),
21656         mag = Math.abs(dir),
21657         new_month, test;
21658         dir = dir > 0 ? 1 : -1;
21659         if (mag == 1){
21660             test = dir == -1
21661             // If going back one month, make sure month is not current month
21662             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21663             ? function(){
21664                 return new_date.getUTCMonth() == month;
21665             }
21666             // If going forward one month, make sure month is as expected
21667             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21668             : function(){
21669                 return new_date.getUTCMonth() != new_month;
21670             };
21671             new_month = month + dir;
21672             new_date.setUTCMonth(new_month);
21673             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21674             if (new_month < 0 || new_month > 11) {
21675                 new_month = (new_month + 12) % 12;
21676             }
21677         } else {
21678             // For magnitudes >1, move one month at a time...
21679             for (var i=0; i<mag; i++) {
21680                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21681                 new_date = this.moveMonth(new_date, dir);
21682             }
21683             // ...then reset the day, keeping it in the new month
21684             new_month = new_date.getUTCMonth();
21685             new_date.setUTCDate(day);
21686             test = function(){
21687                 return new_month != new_date.getUTCMonth();
21688             };
21689         }
21690         // Common date-resetting loop -- if date is beyond end of month, make it
21691         // end of month
21692         while (test()){
21693             new_date.setUTCDate(--day);
21694             new_date.setUTCMonth(new_month);
21695         }
21696         return new_date;
21697     },
21698
21699     moveYear: function(date, dir)
21700     {
21701         return this.moveMonth(date, dir*12);
21702     },
21703
21704     dateWithinRange: function(date)
21705     {
21706         return date >= this.startDate && date <= this.endDate;
21707     },
21708
21709     
21710     remove: function() 
21711     {
21712         this.picker().remove();
21713     },
21714     
21715     validateValue : function(value)
21716     {
21717         if(this.getVisibilityEl().hasClass('hidden')){
21718             return true;
21719         }
21720         
21721         if(value.length < 1)  {
21722             if(this.allowBlank){
21723                 return true;
21724             }
21725             return false;
21726         }
21727         
21728         if(value.length < this.minLength){
21729             return false;
21730         }
21731         if(value.length > this.maxLength){
21732             return false;
21733         }
21734         if(this.vtype){
21735             var vt = Roo.form.VTypes;
21736             if(!vt[this.vtype](value, this)){
21737                 return false;
21738             }
21739         }
21740         if(typeof this.validator == "function"){
21741             var msg = this.validator(value);
21742             if(msg !== true){
21743                 return false;
21744             }
21745         }
21746         
21747         if(this.regex && !this.regex.test(value)){
21748             return false;
21749         }
21750         
21751         if(typeof(this.parseDate(value)) == 'undefined'){
21752             return false;
21753         }
21754         
21755         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21756             return false;
21757         }      
21758         
21759         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21760             return false;
21761         } 
21762         
21763         
21764         return true;
21765     },
21766     
21767     reset : function()
21768     {
21769         this.date = this.viewDate = '';
21770         
21771         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21772     }
21773    
21774 });
21775
21776 Roo.apply(Roo.bootstrap.DateField,  {
21777     
21778     head : {
21779         tag: 'thead',
21780         cn: [
21781         {
21782             tag: 'tr',
21783             cn: [
21784             {
21785                 tag: 'th',
21786                 cls: 'prev',
21787                 html: '<i class="fa fa-arrow-left"/>'
21788             },
21789             {
21790                 tag: 'th',
21791                 cls: 'switch',
21792                 colspan: '5'
21793             },
21794             {
21795                 tag: 'th',
21796                 cls: 'next',
21797                 html: '<i class="fa fa-arrow-right"/>'
21798             }
21799
21800             ]
21801         }
21802         ]
21803     },
21804     
21805     content : {
21806         tag: 'tbody',
21807         cn: [
21808         {
21809             tag: 'tr',
21810             cn: [
21811             {
21812                 tag: 'td',
21813                 colspan: '7'
21814             }
21815             ]
21816         }
21817         ]
21818     },
21819     
21820     footer : {
21821         tag: 'tfoot',
21822         cn: [
21823         {
21824             tag: 'tr',
21825             cn: [
21826             {
21827                 tag: 'th',
21828                 colspan: '7',
21829                 cls: 'today'
21830             }
21831                     
21832             ]
21833         }
21834         ]
21835     },
21836     
21837     dates:{
21838         en: {
21839             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21840             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21841             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21842             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21843             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21844             today: "Today"
21845         }
21846     },
21847     
21848     modes: [
21849     {
21850         clsName: 'days',
21851         navFnc: 'Month',
21852         navStep: 1
21853     },
21854     {
21855         clsName: 'months',
21856         navFnc: 'FullYear',
21857         navStep: 1
21858     },
21859     {
21860         clsName: 'years',
21861         navFnc: 'FullYear',
21862         navStep: 10
21863     }]
21864 });
21865
21866 Roo.apply(Roo.bootstrap.DateField,  {
21867   
21868     template : {
21869         tag: 'div',
21870         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21871         cn: [
21872         {
21873             tag: 'div',
21874             cls: 'datepicker-days',
21875             cn: [
21876             {
21877                 tag: 'table',
21878                 cls: 'table-condensed',
21879                 cn:[
21880                 Roo.bootstrap.DateField.head,
21881                 {
21882                     tag: 'tbody'
21883                 },
21884                 Roo.bootstrap.DateField.footer
21885                 ]
21886             }
21887             ]
21888         },
21889         {
21890             tag: 'div',
21891             cls: 'datepicker-months',
21892             cn: [
21893             {
21894                 tag: 'table',
21895                 cls: 'table-condensed',
21896                 cn:[
21897                 Roo.bootstrap.DateField.head,
21898                 Roo.bootstrap.DateField.content,
21899                 Roo.bootstrap.DateField.footer
21900                 ]
21901             }
21902             ]
21903         },
21904         {
21905             tag: 'div',
21906             cls: 'datepicker-years',
21907             cn: [
21908             {
21909                 tag: 'table',
21910                 cls: 'table-condensed',
21911                 cn:[
21912                 Roo.bootstrap.DateField.head,
21913                 Roo.bootstrap.DateField.content,
21914                 Roo.bootstrap.DateField.footer
21915                 ]
21916             }
21917             ]
21918         }
21919         ]
21920     }
21921 });
21922
21923  
21924
21925  /*
21926  * - LGPL
21927  *
21928  * TimeField
21929  * 
21930  */
21931
21932 /**
21933  * @class Roo.bootstrap.TimeField
21934  * @extends Roo.bootstrap.Input
21935  * Bootstrap DateField class
21936  * 
21937  * 
21938  * @constructor
21939  * Create a new TimeField
21940  * @param {Object} config The config object
21941  */
21942
21943 Roo.bootstrap.TimeField = function(config){
21944     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21945     this.addEvents({
21946             /**
21947              * @event show
21948              * Fires when this field show.
21949              * @param {Roo.bootstrap.DateField} thisthis
21950              * @param {Mixed} date The date value
21951              */
21952             show : true,
21953             /**
21954              * @event show
21955              * Fires when this field hide.
21956              * @param {Roo.bootstrap.DateField} this
21957              * @param {Mixed} date The date value
21958              */
21959             hide : true,
21960             /**
21961              * @event select
21962              * Fires when select a date.
21963              * @param {Roo.bootstrap.DateField} this
21964              * @param {Mixed} date The date value
21965              */
21966             select : true
21967         });
21968 };
21969
21970 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21971     
21972     /**
21973      * @cfg {String} format
21974      * The default time format string which can be overriden for localization support.  The format must be
21975      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21976      */
21977     format : "H:i",
21978
21979     getAutoCreate : function()
21980     {
21981         this.after = '<i class="fa far fa-clock"></i>';
21982         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21983         
21984          
21985     },
21986     onRender: function(ct, position)
21987     {
21988         
21989         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21990                 
21991         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21992         
21993         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21994         
21995         this.pop = this.picker().select('>.datepicker-time',true).first();
21996         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21997         
21998         this.picker().on('mousedown', this.onMousedown, this);
21999         this.picker().on('click', this.onClick, this);
22000         
22001         this.picker().addClass('datepicker-dropdown');
22002     
22003         this.fillTime();
22004         this.update();
22005             
22006         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22007         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22008         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22009         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22010         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22011         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22012
22013     },
22014     
22015     fireKey: function(e){
22016         if (!this.picker().isVisible()){
22017             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22018                 this.show();
22019             }
22020             return;
22021         }
22022
22023         e.preventDefault();
22024         
22025         switch(e.keyCode){
22026             case 27: // escape
22027                 this.hide();
22028                 break;
22029             case 37: // left
22030             case 39: // right
22031                 this.onTogglePeriod();
22032                 break;
22033             case 38: // up
22034                 this.onIncrementMinutes();
22035                 break;
22036             case 40: // down
22037                 this.onDecrementMinutes();
22038                 break;
22039             case 13: // enter
22040             case 9: // tab
22041                 this.setTime();
22042                 break;
22043         }
22044     },
22045     
22046     onClick: function(e) {
22047         e.stopPropagation();
22048         e.preventDefault();
22049     },
22050     
22051     picker : function()
22052     {
22053         return this.pickerEl;
22054     },
22055     
22056     fillTime: function()
22057     {    
22058         var time = this.pop.select('tbody', true).first();
22059         
22060         time.dom.innerHTML = '';
22061         
22062         time.createChild({
22063             tag: 'tr',
22064             cn: [
22065                 {
22066                     tag: 'td',
22067                     cn: [
22068                         {
22069                             tag: 'a',
22070                             href: '#',
22071                             cls: 'btn',
22072                             cn: [
22073                                 {
22074                                     tag: 'i',
22075                                     cls: 'hours-up fa fas fa-chevron-up'
22076                                 }
22077                             ]
22078                         } 
22079                     ]
22080                 },
22081                 {
22082                     tag: 'td',
22083                     cls: 'separator'
22084                 },
22085                 {
22086                     tag: 'td',
22087                     cn: [
22088                         {
22089                             tag: 'a',
22090                             href: '#',
22091                             cls: 'btn',
22092                             cn: [
22093                                 {
22094                                     tag: 'i',
22095                                     cls: 'minutes-up fa fas fa-chevron-up'
22096                                 }
22097                             ]
22098                         }
22099                     ]
22100                 },
22101                 {
22102                     tag: 'td',
22103                     cls: 'separator'
22104                 }
22105             ]
22106         });
22107         
22108         time.createChild({
22109             tag: 'tr',
22110             cn: [
22111                 {
22112                     tag: 'td',
22113                     cn: [
22114                         {
22115                             tag: 'span',
22116                             cls: 'timepicker-hour',
22117                             html: '00'
22118                         }  
22119                     ]
22120                 },
22121                 {
22122                     tag: 'td',
22123                     cls: 'separator',
22124                     html: ':'
22125                 },
22126                 {
22127                     tag: 'td',
22128                     cn: [
22129                         {
22130                             tag: 'span',
22131                             cls: 'timepicker-minute',
22132                             html: '00'
22133                         }  
22134                     ]
22135                 },
22136                 {
22137                     tag: 'td',
22138                     cls: 'separator'
22139                 },
22140                 {
22141                     tag: 'td',
22142                     cn: [
22143                         {
22144                             tag: 'button',
22145                             type: 'button',
22146                             cls: 'btn btn-primary period',
22147                             html: 'AM'
22148                             
22149                         }
22150                     ]
22151                 }
22152             ]
22153         });
22154         
22155         time.createChild({
22156             tag: 'tr',
22157             cn: [
22158                 {
22159                     tag: 'td',
22160                     cn: [
22161                         {
22162                             tag: 'a',
22163                             href: '#',
22164                             cls: 'btn',
22165                             cn: [
22166                                 {
22167                                     tag: 'span',
22168                                     cls: 'hours-down fa fas fa-chevron-down'
22169                                 }
22170                             ]
22171                         }
22172                     ]
22173                 },
22174                 {
22175                     tag: 'td',
22176                     cls: 'separator'
22177                 },
22178                 {
22179                     tag: 'td',
22180                     cn: [
22181                         {
22182                             tag: 'a',
22183                             href: '#',
22184                             cls: 'btn',
22185                             cn: [
22186                                 {
22187                                     tag: 'span',
22188                                     cls: 'minutes-down fa fas fa-chevron-down'
22189                                 }
22190                             ]
22191                         }
22192                     ]
22193                 },
22194                 {
22195                     tag: 'td',
22196                     cls: 'separator'
22197                 }
22198             ]
22199         });
22200         
22201     },
22202     
22203     update: function()
22204     {
22205         
22206         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22207         
22208         this.fill();
22209     },
22210     
22211     fill: function() 
22212     {
22213         var hours = this.time.getHours();
22214         var minutes = this.time.getMinutes();
22215         var period = 'AM';
22216         
22217         if(hours > 11){
22218             period = 'PM';
22219         }
22220         
22221         if(hours == 0){
22222             hours = 12;
22223         }
22224         
22225         
22226         if(hours > 12){
22227             hours = hours - 12;
22228         }
22229         
22230         if(hours < 10){
22231             hours = '0' + hours;
22232         }
22233         
22234         if(minutes < 10){
22235             minutes = '0' + minutes;
22236         }
22237         
22238         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22239         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22240         this.pop.select('button', true).first().dom.innerHTML = period;
22241         
22242     },
22243     
22244     place: function()
22245     {   
22246         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22247         
22248         var cls = ['bottom'];
22249         
22250         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22251             cls.pop();
22252             cls.push('top');
22253         }
22254         
22255         cls.push('right');
22256         
22257         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22258             cls.pop();
22259             cls.push('left');
22260         }
22261         //this.picker().setXY(20000,20000);
22262         this.picker().addClass(cls.join('-'));
22263         
22264         var _this = this;
22265         
22266         Roo.each(cls, function(c){
22267             if(c == 'bottom'){
22268                 (function() {
22269                  //  
22270                 }).defer(200);
22271                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22272                 //_this.picker().setTop(_this.inputEl().getHeight());
22273                 return;
22274             }
22275             if(c == 'top'){
22276                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22277                 
22278                 //_this.picker().setTop(0 - _this.picker().getHeight());
22279                 return;
22280             }
22281             /*
22282             if(c == 'left'){
22283                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22284                 return;
22285             }
22286             if(c == 'right'){
22287                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22288                 return;
22289             }
22290             */
22291         });
22292         
22293     },
22294   
22295     onFocus : function()
22296     {
22297         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22298         this.show();
22299     },
22300     
22301     onBlur : function()
22302     {
22303         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22304         this.hide();
22305     },
22306     
22307     show : function()
22308     {
22309         this.picker().show();
22310         this.pop.show();
22311         this.update();
22312         this.place();
22313         
22314         this.fireEvent('show', this, this.date);
22315     },
22316     
22317     hide : function()
22318     {
22319         this.picker().hide();
22320         this.pop.hide();
22321         
22322         this.fireEvent('hide', this, this.date);
22323     },
22324     
22325     setTime : function()
22326     {
22327         this.hide();
22328         this.setValue(this.time.format(this.format));
22329         
22330         this.fireEvent('select', this, this.date);
22331         
22332         
22333     },
22334     
22335     onMousedown: function(e){
22336         e.stopPropagation();
22337         e.preventDefault();
22338     },
22339     
22340     onIncrementHours: function()
22341     {
22342         Roo.log('onIncrementHours');
22343         this.time = this.time.add(Date.HOUR, 1);
22344         this.update();
22345         
22346     },
22347     
22348     onDecrementHours: function()
22349     {
22350         Roo.log('onDecrementHours');
22351         this.time = this.time.add(Date.HOUR, -1);
22352         this.update();
22353     },
22354     
22355     onIncrementMinutes: function()
22356     {
22357         Roo.log('onIncrementMinutes');
22358         this.time = this.time.add(Date.MINUTE, 1);
22359         this.update();
22360     },
22361     
22362     onDecrementMinutes: function()
22363     {
22364         Roo.log('onDecrementMinutes');
22365         this.time = this.time.add(Date.MINUTE, -1);
22366         this.update();
22367     },
22368     
22369     onTogglePeriod: function()
22370     {
22371         Roo.log('onTogglePeriod');
22372         this.time = this.time.add(Date.HOUR, 12);
22373         this.update();
22374     }
22375     
22376    
22377 });
22378  
22379
22380 Roo.apply(Roo.bootstrap.TimeField,  {
22381   
22382     template : {
22383         tag: 'div',
22384         cls: 'datepicker dropdown-menu',
22385         cn: [
22386             {
22387                 tag: 'div',
22388                 cls: 'datepicker-time',
22389                 cn: [
22390                 {
22391                     tag: 'table',
22392                     cls: 'table-condensed',
22393                     cn:[
22394                         {
22395                             tag: 'tbody',
22396                             cn: [
22397                                 {
22398                                     tag: 'tr',
22399                                     cn: [
22400                                     {
22401                                         tag: 'td',
22402                                         colspan: '7'
22403                                     }
22404                                     ]
22405                                 }
22406                             ]
22407                         },
22408                         {
22409                             tag: 'tfoot',
22410                             cn: [
22411                                 {
22412                                     tag: 'tr',
22413                                     cn: [
22414                                     {
22415                                         tag: 'th',
22416                                         colspan: '7',
22417                                         cls: '',
22418                                         cn: [
22419                                             {
22420                                                 tag: 'button',
22421                                                 cls: 'btn btn-info ok',
22422                                                 html: 'OK'
22423                                             }
22424                                         ]
22425                                     }
22426                     
22427                                     ]
22428                                 }
22429                             ]
22430                         }
22431                     ]
22432                 }
22433                 ]
22434             }
22435         ]
22436     }
22437 });
22438
22439  
22440
22441  /*
22442  * - LGPL
22443  *
22444  * MonthField
22445  * 
22446  */
22447
22448 /**
22449  * @class Roo.bootstrap.MonthField
22450  * @extends Roo.bootstrap.Input
22451  * Bootstrap MonthField class
22452  * 
22453  * @cfg {String} language default en
22454  * 
22455  * @constructor
22456  * Create a new MonthField
22457  * @param {Object} config The config object
22458  */
22459
22460 Roo.bootstrap.MonthField = function(config){
22461     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22462     
22463     this.addEvents({
22464         /**
22465          * @event show
22466          * Fires when this field show.
22467          * @param {Roo.bootstrap.MonthField} this
22468          * @param {Mixed} date The date value
22469          */
22470         show : true,
22471         /**
22472          * @event show
22473          * Fires when this field hide.
22474          * @param {Roo.bootstrap.MonthField} this
22475          * @param {Mixed} date The date value
22476          */
22477         hide : true,
22478         /**
22479          * @event select
22480          * Fires when select a date.
22481          * @param {Roo.bootstrap.MonthField} this
22482          * @param {String} oldvalue The old value
22483          * @param {String} newvalue The new value
22484          */
22485         select : true
22486     });
22487 };
22488
22489 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22490     
22491     onRender: function(ct, position)
22492     {
22493         
22494         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22495         
22496         this.language = this.language || 'en';
22497         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22498         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22499         
22500         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22501         this.isInline = false;
22502         this.isInput = true;
22503         this.component = this.el.select('.add-on', true).first() || false;
22504         this.component = (this.component && this.component.length === 0) ? false : this.component;
22505         this.hasInput = this.component && this.inputEL().length;
22506         
22507         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22508         
22509         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22510         
22511         this.picker().on('mousedown', this.onMousedown, this);
22512         this.picker().on('click', this.onClick, this);
22513         
22514         this.picker().addClass('datepicker-dropdown');
22515         
22516         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22517             v.setStyle('width', '189px');
22518         });
22519         
22520         this.fillMonths();
22521         
22522         this.update();
22523         
22524         if(this.isInline) {
22525             this.show();
22526         }
22527         
22528     },
22529     
22530     setValue: function(v, suppressEvent)
22531     {   
22532         var o = this.getValue();
22533         
22534         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22535         
22536         this.update();
22537
22538         if(suppressEvent !== true){
22539             this.fireEvent('select', this, o, v);
22540         }
22541         
22542     },
22543     
22544     getValue: function()
22545     {
22546         return this.value;
22547     },
22548     
22549     onClick: function(e) 
22550     {
22551         e.stopPropagation();
22552         e.preventDefault();
22553         
22554         var target = e.getTarget();
22555         
22556         if(target.nodeName.toLowerCase() === 'i'){
22557             target = Roo.get(target).dom.parentNode;
22558         }
22559         
22560         var nodeName = target.nodeName;
22561         var className = target.className;
22562         var html = target.innerHTML;
22563         
22564         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22565             return;
22566         }
22567         
22568         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22569         
22570         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22571         
22572         this.hide();
22573                         
22574     },
22575     
22576     picker : function()
22577     {
22578         return this.pickerEl;
22579     },
22580     
22581     fillMonths: function()
22582     {    
22583         var i = 0;
22584         var months = this.picker().select('>.datepicker-months td', true).first();
22585         
22586         months.dom.innerHTML = '';
22587         
22588         while (i < 12) {
22589             var month = {
22590                 tag: 'span',
22591                 cls: 'month',
22592                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22593             };
22594             
22595             months.createChild(month);
22596         }
22597         
22598     },
22599     
22600     update: function()
22601     {
22602         var _this = this;
22603         
22604         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22605             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22606         }
22607         
22608         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22609             e.removeClass('active');
22610             
22611             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22612                 e.addClass('active');
22613             }
22614         })
22615     },
22616     
22617     place: function()
22618     {
22619         if(this.isInline) {
22620             return;
22621         }
22622         
22623         this.picker().removeClass(['bottom', 'top']);
22624         
22625         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22626             /*
22627              * place to the top of element!
22628              *
22629              */
22630             
22631             this.picker().addClass('top');
22632             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22633             
22634             return;
22635         }
22636         
22637         this.picker().addClass('bottom');
22638         
22639         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22640     },
22641     
22642     onFocus : function()
22643     {
22644         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22645         this.show();
22646     },
22647     
22648     onBlur : function()
22649     {
22650         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22651         
22652         var d = this.inputEl().getValue();
22653         
22654         this.setValue(d);
22655                 
22656         this.hide();
22657     },
22658     
22659     show : function()
22660     {
22661         this.picker().show();
22662         this.picker().select('>.datepicker-months', true).first().show();
22663         this.update();
22664         this.place();
22665         
22666         this.fireEvent('show', this, this.date);
22667     },
22668     
22669     hide : function()
22670     {
22671         if(this.isInline) {
22672             return;
22673         }
22674         this.picker().hide();
22675         this.fireEvent('hide', this, this.date);
22676         
22677     },
22678     
22679     onMousedown: function(e)
22680     {
22681         e.stopPropagation();
22682         e.preventDefault();
22683     },
22684     
22685     keyup: function(e)
22686     {
22687         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22688         this.update();
22689     },
22690
22691     fireKey: function(e)
22692     {
22693         if (!this.picker().isVisible()){
22694             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22695                 this.show();
22696             }
22697             return;
22698         }
22699         
22700         var dir;
22701         
22702         switch(e.keyCode){
22703             case 27: // escape
22704                 this.hide();
22705                 e.preventDefault();
22706                 break;
22707             case 37: // left
22708             case 39: // right
22709                 dir = e.keyCode == 37 ? -1 : 1;
22710                 
22711                 this.vIndex = this.vIndex + dir;
22712                 
22713                 if(this.vIndex < 0){
22714                     this.vIndex = 0;
22715                 }
22716                 
22717                 if(this.vIndex > 11){
22718                     this.vIndex = 11;
22719                 }
22720                 
22721                 if(isNaN(this.vIndex)){
22722                     this.vIndex = 0;
22723                 }
22724                 
22725                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22726                 
22727                 break;
22728             case 38: // up
22729             case 40: // down
22730                 
22731                 dir = e.keyCode == 38 ? -1 : 1;
22732                 
22733                 this.vIndex = this.vIndex + dir * 4;
22734                 
22735                 if(this.vIndex < 0){
22736                     this.vIndex = 0;
22737                 }
22738                 
22739                 if(this.vIndex > 11){
22740                     this.vIndex = 11;
22741                 }
22742                 
22743                 if(isNaN(this.vIndex)){
22744                     this.vIndex = 0;
22745                 }
22746                 
22747                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22748                 break;
22749                 
22750             case 13: // enter
22751                 
22752                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22753                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22754                 }
22755                 
22756                 this.hide();
22757                 e.preventDefault();
22758                 break;
22759             case 9: // tab
22760                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22761                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22762                 }
22763                 this.hide();
22764                 break;
22765             case 16: // shift
22766             case 17: // ctrl
22767             case 18: // alt
22768                 break;
22769             default :
22770                 this.hide();
22771                 
22772         }
22773     },
22774     
22775     remove: function() 
22776     {
22777         this.picker().remove();
22778     }
22779    
22780 });
22781
22782 Roo.apply(Roo.bootstrap.MonthField,  {
22783     
22784     content : {
22785         tag: 'tbody',
22786         cn: [
22787         {
22788             tag: 'tr',
22789             cn: [
22790             {
22791                 tag: 'td',
22792                 colspan: '7'
22793             }
22794             ]
22795         }
22796         ]
22797     },
22798     
22799     dates:{
22800         en: {
22801             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22802             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22803         }
22804     }
22805 });
22806
22807 Roo.apply(Roo.bootstrap.MonthField,  {
22808   
22809     template : {
22810         tag: 'div',
22811         cls: 'datepicker dropdown-menu roo-dynamic',
22812         cn: [
22813             {
22814                 tag: 'div',
22815                 cls: 'datepicker-months',
22816                 cn: [
22817                 {
22818                     tag: 'table',
22819                     cls: 'table-condensed',
22820                     cn:[
22821                         Roo.bootstrap.DateField.content
22822                     ]
22823                 }
22824                 ]
22825             }
22826         ]
22827     }
22828 });
22829
22830  
22831
22832  
22833  /*
22834  * - LGPL
22835  *
22836  * CheckBox
22837  * 
22838  */
22839
22840 /**
22841  * @class Roo.bootstrap.CheckBox
22842  * @extends Roo.bootstrap.Input
22843  * Bootstrap CheckBox class
22844  * 
22845  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22846  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22847  * @cfg {String} boxLabel The text that appears beside the checkbox
22848  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22849  * @cfg {Boolean} checked initnal the element
22850  * @cfg {Boolean} inline inline the element (default false)
22851  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22852  * @cfg {String} tooltip label tooltip
22853  * 
22854  * @constructor
22855  * Create a new CheckBox
22856  * @param {Object} config The config object
22857  */
22858
22859 Roo.bootstrap.CheckBox = function(config){
22860     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22861    
22862     this.addEvents({
22863         /**
22864         * @event check
22865         * Fires when the element is checked or unchecked.
22866         * @param {Roo.bootstrap.CheckBox} this This input
22867         * @param {Boolean} checked The new checked value
22868         */
22869        check : true,
22870        /**
22871         * @event click
22872         * Fires when the element is click.
22873         * @param {Roo.bootstrap.CheckBox} this This input
22874         */
22875        click : true
22876     });
22877     
22878 };
22879
22880 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22881   
22882     inputType: 'checkbox',
22883     inputValue: 1,
22884     valueOff: 0,
22885     boxLabel: false,
22886     checked: false,
22887     weight : false,
22888     inline: false,
22889     tooltip : '',
22890     
22891     // checkbox success does not make any sense really.. 
22892     invalidClass : "",
22893     validClass : "",
22894     
22895     
22896     getAutoCreate : function()
22897     {
22898         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22899         
22900         var id = Roo.id();
22901         
22902         var cfg = {};
22903         
22904         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22905         
22906         if(this.inline){
22907             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22908         }
22909         
22910         var input =  {
22911             tag: 'input',
22912             id : id,
22913             type : this.inputType,
22914             value : this.inputValue,
22915             cls : 'roo-' + this.inputType, //'form-box',
22916             placeholder : this.placeholder || ''
22917             
22918         };
22919         
22920         if(this.inputType != 'radio'){
22921             var hidden =  {
22922                 tag: 'input',
22923                 type : 'hidden',
22924                 cls : 'roo-hidden-value',
22925                 value : this.checked ? this.inputValue : this.valueOff
22926             };
22927         }
22928         
22929             
22930         if (this.weight) { // Validity check?
22931             cfg.cls += " " + this.inputType + "-" + this.weight;
22932         }
22933         
22934         if (this.disabled) {
22935             input.disabled=true;
22936         }
22937         
22938         if(this.checked){
22939             input.checked = this.checked;
22940         }
22941         
22942         if (this.name) {
22943             
22944             input.name = this.name;
22945             
22946             if(this.inputType != 'radio'){
22947                 hidden.name = this.name;
22948                 input.name = '_hidden_' + this.name;
22949             }
22950         }
22951         
22952         if (this.size) {
22953             input.cls += ' input-' + this.size;
22954         }
22955         
22956         var settings=this;
22957         
22958         ['xs','sm','md','lg'].map(function(size){
22959             if (settings[size]) {
22960                 cfg.cls += ' col-' + size + '-' + settings[size];
22961             }
22962         });
22963         
22964         var inputblock = input;
22965          
22966         if (this.before || this.after) {
22967             
22968             inputblock = {
22969                 cls : 'input-group',
22970                 cn :  [] 
22971             };
22972             
22973             if (this.before) {
22974                 inputblock.cn.push({
22975                     tag :'span',
22976                     cls : 'input-group-addon',
22977                     html : this.before
22978                 });
22979             }
22980             
22981             inputblock.cn.push(input);
22982             
22983             if(this.inputType != 'radio'){
22984                 inputblock.cn.push(hidden);
22985             }
22986             
22987             if (this.after) {
22988                 inputblock.cn.push({
22989                     tag :'span',
22990                     cls : 'input-group-addon',
22991                     html : this.after
22992                 });
22993             }
22994             
22995         }
22996         var boxLabelCfg = false;
22997         
22998         if(this.boxLabel){
22999            
23000             boxLabelCfg = {
23001                 tag: 'label',
23002                 //'for': id, // box label is handled by onclick - so no for...
23003                 cls: 'box-label',
23004                 html: this.boxLabel
23005             };
23006             if(this.tooltip){
23007                 boxLabelCfg.tooltip = this.tooltip;
23008             }
23009              
23010         }
23011         
23012         
23013         if (align ==='left' && this.fieldLabel.length) {
23014 //                Roo.log("left and has label");
23015             cfg.cn = [
23016                 {
23017                     tag: 'label',
23018                     'for' :  id,
23019                     cls : 'control-label',
23020                     html : this.fieldLabel
23021                 },
23022                 {
23023                     cls : "", 
23024                     cn: [
23025                         inputblock
23026                     ]
23027                 }
23028             ];
23029             
23030             if (boxLabelCfg) {
23031                 cfg.cn[1].cn.push(boxLabelCfg);
23032             }
23033             
23034             if(this.labelWidth > 12){
23035                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23036             }
23037             
23038             if(this.labelWidth < 13 && this.labelmd == 0){
23039                 this.labelmd = this.labelWidth;
23040             }
23041             
23042             if(this.labellg > 0){
23043                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23044                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23045             }
23046             
23047             if(this.labelmd > 0){
23048                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23049                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23050             }
23051             
23052             if(this.labelsm > 0){
23053                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23054                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23055             }
23056             
23057             if(this.labelxs > 0){
23058                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23059                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23060             }
23061             
23062         } else if ( this.fieldLabel.length) {
23063 //                Roo.log(" label");
23064                 cfg.cn = [
23065                    
23066                     {
23067                         tag: this.boxLabel ? 'span' : 'label',
23068                         'for': id,
23069                         cls: 'control-label box-input-label',
23070                         //cls : 'input-group-addon',
23071                         html : this.fieldLabel
23072                     },
23073                     
23074                     inputblock
23075                     
23076                 ];
23077                 if (boxLabelCfg) {
23078                     cfg.cn.push(boxLabelCfg);
23079                 }
23080
23081         } else {
23082             
23083 //                Roo.log(" no label && no align");
23084                 cfg.cn = [  inputblock ] ;
23085                 if (boxLabelCfg) {
23086                     cfg.cn.push(boxLabelCfg);
23087                 }
23088
23089                 
23090         }
23091         
23092        
23093         
23094         if(this.inputType != 'radio'){
23095             cfg.cn.push(hidden);
23096         }
23097         
23098         return cfg;
23099         
23100     },
23101     
23102     /**
23103      * return the real input element.
23104      */
23105     inputEl: function ()
23106     {
23107         return this.el.select('input.roo-' + this.inputType,true).first();
23108     },
23109     hiddenEl: function ()
23110     {
23111         return this.el.select('input.roo-hidden-value',true).first();
23112     },
23113     
23114     labelEl: function()
23115     {
23116         return this.el.select('label.control-label',true).first();
23117     },
23118     /* depricated... */
23119     
23120     label: function()
23121     {
23122         return this.labelEl();
23123     },
23124     
23125     boxLabelEl: function()
23126     {
23127         return this.el.select('label.box-label',true).first();
23128     },
23129     
23130     initEvents : function()
23131     {
23132 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23133         
23134         this.inputEl().on('click', this.onClick,  this);
23135         
23136         if (this.boxLabel) { 
23137             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23138         }
23139         
23140         this.startValue = this.getValue();
23141         
23142         if(this.groupId){
23143             Roo.bootstrap.CheckBox.register(this);
23144         }
23145     },
23146     
23147     onClick : function(e)
23148     {   
23149         if(this.fireEvent('click', this, e) !== false){
23150             this.setChecked(!this.checked);
23151         }
23152         
23153     },
23154     
23155     setChecked : function(state,suppressEvent)
23156     {
23157         this.startValue = this.getValue();
23158
23159         if(this.inputType == 'radio'){
23160             
23161             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23162                 e.dom.checked = false;
23163             });
23164             
23165             this.inputEl().dom.checked = true;
23166             
23167             this.inputEl().dom.value = this.inputValue;
23168             
23169             if(suppressEvent !== true){
23170                 this.fireEvent('check', this, true);
23171             }
23172             
23173             this.validate();
23174             
23175             return;
23176         }
23177         
23178         this.checked = state;
23179         
23180         this.inputEl().dom.checked = state;
23181         
23182         
23183         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23184         
23185         if(suppressEvent !== true){
23186             this.fireEvent('check', this, state);
23187         }
23188         
23189         this.validate();
23190     },
23191     
23192     getValue : function()
23193     {
23194         if(this.inputType == 'radio'){
23195             return this.getGroupValue();
23196         }
23197         
23198         return this.hiddenEl().dom.value;
23199         
23200     },
23201     
23202     getGroupValue : function()
23203     {
23204         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23205             return '';
23206         }
23207         
23208         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23209     },
23210     
23211     setValue : function(v,suppressEvent)
23212     {
23213         if(this.inputType == 'radio'){
23214             this.setGroupValue(v, suppressEvent);
23215             return;
23216         }
23217         
23218         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23219         
23220         this.validate();
23221     },
23222     
23223     setGroupValue : function(v, suppressEvent)
23224     {
23225         this.startValue = this.getValue();
23226         
23227         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23228             e.dom.checked = false;
23229             
23230             if(e.dom.value == v){
23231                 e.dom.checked = true;
23232             }
23233         });
23234         
23235         if(suppressEvent !== true){
23236             this.fireEvent('check', this, true);
23237         }
23238
23239         this.validate();
23240         
23241         return;
23242     },
23243     
23244     validate : function()
23245     {
23246         if(this.getVisibilityEl().hasClass('hidden')){
23247             return true;
23248         }
23249         
23250         if(
23251                 this.disabled || 
23252                 (this.inputType == 'radio' && this.validateRadio()) ||
23253                 (this.inputType == 'checkbox' && this.validateCheckbox())
23254         ){
23255             this.markValid();
23256             return true;
23257         }
23258         
23259         this.markInvalid();
23260         return false;
23261     },
23262     
23263     validateRadio : function()
23264     {
23265         if(this.getVisibilityEl().hasClass('hidden')){
23266             return true;
23267         }
23268         
23269         if(this.allowBlank){
23270             return true;
23271         }
23272         
23273         var valid = false;
23274         
23275         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23276             if(!e.dom.checked){
23277                 return;
23278             }
23279             
23280             valid = true;
23281             
23282             return false;
23283         });
23284         
23285         return valid;
23286     },
23287     
23288     validateCheckbox : function()
23289     {
23290         if(!this.groupId){
23291             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23292             //return (this.getValue() == this.inputValue) ? true : false;
23293         }
23294         
23295         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23296         
23297         if(!group){
23298             return false;
23299         }
23300         
23301         var r = false;
23302         
23303         for(var i in group){
23304             if(group[i].el.isVisible(true)){
23305                 r = false;
23306                 break;
23307             }
23308             
23309             r = true;
23310         }
23311         
23312         for(var i in group){
23313             if(r){
23314                 break;
23315             }
23316             
23317             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23318         }
23319         
23320         return r;
23321     },
23322     
23323     /**
23324      * Mark this field as valid
23325      */
23326     markValid : function()
23327     {
23328         var _this = this;
23329         
23330         this.fireEvent('valid', this);
23331         
23332         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23333         
23334         if(this.groupId){
23335             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23336         }
23337         
23338         if(label){
23339             label.markValid();
23340         }
23341
23342         if(this.inputType == 'radio'){
23343             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23344                 var fg = e.findParent('.form-group', false, true);
23345                 if (Roo.bootstrap.version == 3) {
23346                     fg.removeClass([_this.invalidClass, _this.validClass]);
23347                     fg.addClass(_this.validClass);
23348                 } else {
23349                     fg.removeClass(['is-valid', 'is-invalid']);
23350                     fg.addClass('is-valid');
23351                 }
23352             });
23353             
23354             return;
23355         }
23356
23357         if(!this.groupId){
23358             var fg = this.el.findParent('.form-group', false, true);
23359             if (Roo.bootstrap.version == 3) {
23360                 fg.removeClass([this.invalidClass, this.validClass]);
23361                 fg.addClass(this.validClass);
23362             } else {
23363                 fg.removeClass(['is-valid', 'is-invalid']);
23364                 fg.addClass('is-valid');
23365             }
23366             return;
23367         }
23368         
23369         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23370         
23371         if(!group){
23372             return;
23373         }
23374         
23375         for(var i in group){
23376             var fg = group[i].el.findParent('.form-group', false, true);
23377             if (Roo.bootstrap.version == 3) {
23378                 fg.removeClass([this.invalidClass, this.validClass]);
23379                 fg.addClass(this.validClass);
23380             } else {
23381                 fg.removeClass(['is-valid', 'is-invalid']);
23382                 fg.addClass('is-valid');
23383             }
23384         }
23385     },
23386     
23387      /**
23388      * Mark this field as invalid
23389      * @param {String} msg The validation message
23390      */
23391     markInvalid : function(msg)
23392     {
23393         if(this.allowBlank){
23394             return;
23395         }
23396         
23397         var _this = this;
23398         
23399         this.fireEvent('invalid', this, msg);
23400         
23401         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23402         
23403         if(this.groupId){
23404             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23405         }
23406         
23407         if(label){
23408             label.markInvalid();
23409         }
23410             
23411         if(this.inputType == 'radio'){
23412             
23413             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23414                 var fg = e.findParent('.form-group', false, true);
23415                 if (Roo.bootstrap.version == 3) {
23416                     fg.removeClass([_this.invalidClass, _this.validClass]);
23417                     fg.addClass(_this.invalidClass);
23418                 } else {
23419                     fg.removeClass(['is-invalid', 'is-valid']);
23420                     fg.addClass('is-invalid');
23421                 }
23422             });
23423             
23424             return;
23425         }
23426         
23427         if(!this.groupId){
23428             var fg = this.el.findParent('.form-group', false, true);
23429             if (Roo.bootstrap.version == 3) {
23430                 fg.removeClass([_this.invalidClass, _this.validClass]);
23431                 fg.addClass(_this.invalidClass);
23432             } else {
23433                 fg.removeClass(['is-invalid', 'is-valid']);
23434                 fg.addClass('is-invalid');
23435             }
23436             return;
23437         }
23438         
23439         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23440         
23441         if(!group){
23442             return;
23443         }
23444         
23445         for(var i in group){
23446             var fg = group[i].el.findParent('.form-group', false, true);
23447             if (Roo.bootstrap.version == 3) {
23448                 fg.removeClass([_this.invalidClass, _this.validClass]);
23449                 fg.addClass(_this.invalidClass);
23450             } else {
23451                 fg.removeClass(['is-invalid', 'is-valid']);
23452                 fg.addClass('is-invalid');
23453             }
23454         }
23455         
23456     },
23457     
23458     clearInvalid : function()
23459     {
23460         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23461         
23462         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23463         
23464         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23465         
23466         if (label && label.iconEl) {
23467             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23468             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23469         }
23470     },
23471     
23472     disable : function()
23473     {
23474         if(this.inputType != 'radio'){
23475             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23476             return;
23477         }
23478         
23479         var _this = this;
23480         
23481         if(this.rendered){
23482             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23483                 _this.getActionEl().addClass(this.disabledClass);
23484                 e.dom.disabled = true;
23485             });
23486         }
23487         
23488         this.disabled = true;
23489         this.fireEvent("disable", this);
23490         return this;
23491     },
23492
23493     enable : function()
23494     {
23495         if(this.inputType != 'radio'){
23496             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23497             return;
23498         }
23499         
23500         var _this = this;
23501         
23502         if(this.rendered){
23503             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23504                 _this.getActionEl().removeClass(this.disabledClass);
23505                 e.dom.disabled = false;
23506             });
23507         }
23508         
23509         this.disabled = false;
23510         this.fireEvent("enable", this);
23511         return this;
23512     },
23513     
23514     setBoxLabel : function(v)
23515     {
23516         this.boxLabel = v;
23517         
23518         if(this.rendered){
23519             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23520         }
23521     }
23522
23523 });
23524
23525 Roo.apply(Roo.bootstrap.CheckBox, {
23526     
23527     groups: {},
23528     
23529      /**
23530     * register a CheckBox Group
23531     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23532     */
23533     register : function(checkbox)
23534     {
23535         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23536             this.groups[checkbox.groupId] = {};
23537         }
23538         
23539         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23540             return;
23541         }
23542         
23543         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23544         
23545     },
23546     /**
23547     * fetch a CheckBox Group based on the group ID
23548     * @param {string} the group ID
23549     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23550     */
23551     get: function(groupId) {
23552         if (typeof(this.groups[groupId]) == 'undefined') {
23553             return false;
23554         }
23555         
23556         return this.groups[groupId] ;
23557     }
23558     
23559     
23560 });
23561 /*
23562  * - LGPL
23563  *
23564  * RadioItem
23565  * 
23566  */
23567
23568 /**
23569  * @class Roo.bootstrap.Radio
23570  * @extends Roo.bootstrap.Component
23571  * Bootstrap Radio class
23572  * @cfg {String} boxLabel - the label associated
23573  * @cfg {String} value - the value of radio
23574  * 
23575  * @constructor
23576  * Create a new Radio
23577  * @param {Object} config The config object
23578  */
23579 Roo.bootstrap.Radio = function(config){
23580     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23581     
23582 };
23583
23584 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23585     
23586     boxLabel : '',
23587     
23588     value : '',
23589     
23590     getAutoCreate : function()
23591     {
23592         var cfg = {
23593             tag : 'div',
23594             cls : 'form-group radio',
23595             cn : [
23596                 {
23597                     tag : 'label',
23598                     cls : 'box-label',
23599                     html : this.boxLabel
23600                 }
23601             ]
23602         };
23603         
23604         return cfg;
23605     },
23606     
23607     initEvents : function() 
23608     {
23609         this.parent().register(this);
23610         
23611         this.el.on('click', this.onClick, this);
23612         
23613     },
23614     
23615     onClick : function(e)
23616     {
23617         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23618             this.setChecked(true);
23619         }
23620     },
23621     
23622     setChecked : function(state, suppressEvent)
23623     {
23624         this.parent().setValue(this.value, suppressEvent);
23625         
23626     },
23627     
23628     setBoxLabel : function(v)
23629     {
23630         this.boxLabel = v;
23631         
23632         if(this.rendered){
23633             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23634         }
23635     }
23636     
23637 });
23638  
23639
23640  /*
23641  * - LGPL
23642  *
23643  * Input
23644  * 
23645  */
23646
23647 /**
23648  * @class Roo.bootstrap.SecurePass
23649  * @extends Roo.bootstrap.Input
23650  * Bootstrap SecurePass class
23651  *
23652  * 
23653  * @constructor
23654  * Create a new SecurePass
23655  * @param {Object} config The config object
23656  */
23657  
23658 Roo.bootstrap.SecurePass = function (config) {
23659     // these go here, so the translation tool can replace them..
23660     this.errors = {
23661         PwdEmpty: "Please type a password, and then retype it to confirm.",
23662         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23663         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23664         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23665         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23666         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23667         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23668         TooWeak: "Your password is Too Weak."
23669     },
23670     this.meterLabel = "Password strength:";
23671     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23672     this.meterClass = [
23673         "roo-password-meter-tooweak", 
23674         "roo-password-meter-weak", 
23675         "roo-password-meter-medium", 
23676         "roo-password-meter-strong", 
23677         "roo-password-meter-grey"
23678     ];
23679     
23680     this.errors = {};
23681     
23682     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23683 }
23684
23685 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23686     /**
23687      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23688      * {
23689      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23690      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23691      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23692      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23693      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23694      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23695      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23696      * })
23697      */
23698     // private
23699     
23700     meterWidth: 300,
23701     errorMsg :'',    
23702     errors: false,
23703     imageRoot: '/',
23704     /**
23705      * @cfg {String/Object} Label for the strength meter (defaults to
23706      * 'Password strength:')
23707      */
23708     // private
23709     meterLabel: '',
23710     /**
23711      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23712      * ['Weak', 'Medium', 'Strong'])
23713      */
23714     // private    
23715     pwdStrengths: false,    
23716     // private
23717     strength: 0,
23718     // private
23719     _lastPwd: null,
23720     // private
23721     kCapitalLetter: 0,
23722     kSmallLetter: 1,
23723     kDigit: 2,
23724     kPunctuation: 3,
23725     
23726     insecure: false,
23727     // private
23728     initEvents: function ()
23729     {
23730         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23731
23732         if (this.el.is('input[type=password]') && Roo.isSafari) {
23733             this.el.on('keydown', this.SafariOnKeyDown, this);
23734         }
23735
23736         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23737     },
23738     // private
23739     onRender: function (ct, position)
23740     {
23741         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23742         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23743         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23744
23745         this.trigger.createChild({
23746                    cn: [
23747                     {
23748                     //id: 'PwdMeter',
23749                     tag: 'div',
23750                     cls: 'roo-password-meter-grey col-xs-12',
23751                     style: {
23752                         //width: 0,
23753                         //width: this.meterWidth + 'px'                                                
23754                         }
23755                     },
23756                     {                            
23757                          cls: 'roo-password-meter-text'                          
23758                     }
23759                 ]            
23760         });
23761
23762          
23763         if (this.hideTrigger) {
23764             this.trigger.setDisplayed(false);
23765         }
23766         this.setSize(this.width || '', this.height || '');
23767     },
23768     // private
23769     onDestroy: function ()
23770     {
23771         if (this.trigger) {
23772             this.trigger.removeAllListeners();
23773             this.trigger.remove();
23774         }
23775         if (this.wrap) {
23776             this.wrap.remove();
23777         }
23778         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23779     },
23780     // private
23781     checkStrength: function ()
23782     {
23783         var pwd = this.inputEl().getValue();
23784         if (pwd == this._lastPwd) {
23785             return;
23786         }
23787
23788         var strength;
23789         if (this.ClientSideStrongPassword(pwd)) {
23790             strength = 3;
23791         } else if (this.ClientSideMediumPassword(pwd)) {
23792             strength = 2;
23793         } else if (this.ClientSideWeakPassword(pwd)) {
23794             strength = 1;
23795         } else {
23796             strength = 0;
23797         }
23798         
23799         Roo.log('strength1: ' + strength);
23800         
23801         //var pm = this.trigger.child('div/div/div').dom;
23802         var pm = this.trigger.child('div/div');
23803         pm.removeClass(this.meterClass);
23804         pm.addClass(this.meterClass[strength]);
23805                 
23806         
23807         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23808                 
23809         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23810         
23811         this._lastPwd = pwd;
23812     },
23813     reset: function ()
23814     {
23815         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23816         
23817         this._lastPwd = '';
23818         
23819         var pm = this.trigger.child('div/div');
23820         pm.removeClass(this.meterClass);
23821         pm.addClass('roo-password-meter-grey');        
23822         
23823         
23824         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23825         
23826         pt.innerHTML = '';
23827         this.inputEl().dom.type='password';
23828     },
23829     // private
23830     validateValue: function (value)
23831     {
23832         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23833             return false;
23834         }
23835         if (value.length == 0) {
23836             if (this.allowBlank) {
23837                 this.clearInvalid();
23838                 return true;
23839             }
23840
23841             this.markInvalid(this.errors.PwdEmpty);
23842             this.errorMsg = this.errors.PwdEmpty;
23843             return false;
23844         }
23845         
23846         if(this.insecure){
23847             return true;
23848         }
23849         
23850         if (!value.match(/[\x21-\x7e]+/)) {
23851             this.markInvalid(this.errors.PwdBadChar);
23852             this.errorMsg = this.errors.PwdBadChar;
23853             return false;
23854         }
23855         if (value.length < 6) {
23856             this.markInvalid(this.errors.PwdShort);
23857             this.errorMsg = this.errors.PwdShort;
23858             return false;
23859         }
23860         if (value.length > 16) {
23861             this.markInvalid(this.errors.PwdLong);
23862             this.errorMsg = this.errors.PwdLong;
23863             return false;
23864         }
23865         var strength;
23866         if (this.ClientSideStrongPassword(value)) {
23867             strength = 3;
23868         } else if (this.ClientSideMediumPassword(value)) {
23869             strength = 2;
23870         } else if (this.ClientSideWeakPassword(value)) {
23871             strength = 1;
23872         } else {
23873             strength = 0;
23874         }
23875
23876         
23877         if (strength < 2) {
23878             //this.markInvalid(this.errors.TooWeak);
23879             this.errorMsg = this.errors.TooWeak;
23880             //return false;
23881         }
23882         
23883         
23884         console.log('strength2: ' + strength);
23885         
23886         //var pm = this.trigger.child('div/div/div').dom;
23887         
23888         var pm = this.trigger.child('div/div');
23889         pm.removeClass(this.meterClass);
23890         pm.addClass(this.meterClass[strength]);
23891                 
23892         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23893                 
23894         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23895         
23896         this.errorMsg = ''; 
23897         return true;
23898     },
23899     // private
23900     CharacterSetChecks: function (type)
23901     {
23902         this.type = type;
23903         this.fResult = false;
23904     },
23905     // private
23906     isctype: function (character, type)
23907     {
23908         switch (type) {  
23909             case this.kCapitalLetter:
23910                 if (character >= 'A' && character <= 'Z') {
23911                     return true;
23912                 }
23913                 break;
23914             
23915             case this.kSmallLetter:
23916                 if (character >= 'a' && character <= 'z') {
23917                     return true;
23918                 }
23919                 break;
23920             
23921             case this.kDigit:
23922                 if (character >= '0' && character <= '9') {
23923                     return true;
23924                 }
23925                 break;
23926             
23927             case this.kPunctuation:
23928                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23929                     return true;
23930                 }
23931                 break;
23932             
23933             default:
23934                 return false;
23935         }
23936
23937     },
23938     // private
23939     IsLongEnough: function (pwd, size)
23940     {
23941         return !(pwd == null || isNaN(size) || pwd.length < size);
23942     },
23943     // private
23944     SpansEnoughCharacterSets: function (word, nb)
23945     {
23946         if (!this.IsLongEnough(word, nb))
23947         {
23948             return false;
23949         }
23950
23951         var characterSetChecks = new Array(
23952             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23953             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23954         );
23955         
23956         for (var index = 0; index < word.length; ++index) {
23957             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23958                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23959                     characterSetChecks[nCharSet].fResult = true;
23960                     break;
23961                 }
23962             }
23963         }
23964
23965         var nCharSets = 0;
23966         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23967             if (characterSetChecks[nCharSet].fResult) {
23968                 ++nCharSets;
23969             }
23970         }
23971
23972         if (nCharSets < nb) {
23973             return false;
23974         }
23975         return true;
23976     },
23977     // private
23978     ClientSideStrongPassword: function (pwd)
23979     {
23980         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23981     },
23982     // private
23983     ClientSideMediumPassword: function (pwd)
23984     {
23985         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23986     },
23987     // private
23988     ClientSideWeakPassword: function (pwd)
23989     {
23990         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23991     }
23992           
23993 })//<script type="text/javascript">
23994
23995 /*
23996  * Based  Ext JS Library 1.1.1
23997  * Copyright(c) 2006-2007, Ext JS, LLC.
23998  * LGPL
23999  *
24000  */
24001  
24002 /**
24003  * @class Roo.HtmlEditorCore
24004  * @extends Roo.Component
24005  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24006  *
24007  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24008  */
24009
24010 Roo.HtmlEditorCore = function(config){
24011     
24012     
24013     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24014     
24015     
24016     this.addEvents({
24017         /**
24018          * @event initialize
24019          * Fires when the editor is fully initialized (including the iframe)
24020          * @param {Roo.HtmlEditorCore} this
24021          */
24022         initialize: true,
24023         /**
24024          * @event activate
24025          * Fires when the editor is first receives the focus. Any insertion must wait
24026          * until after this event.
24027          * @param {Roo.HtmlEditorCore} this
24028          */
24029         activate: true,
24030          /**
24031          * @event beforesync
24032          * Fires before the textarea is updated with content from the editor iframe. Return false
24033          * to cancel the sync.
24034          * @param {Roo.HtmlEditorCore} this
24035          * @param {String} html
24036          */
24037         beforesync: true,
24038          /**
24039          * @event beforepush
24040          * Fires before the iframe editor is updated with content from the textarea. Return false
24041          * to cancel the push.
24042          * @param {Roo.HtmlEditorCore} this
24043          * @param {String} html
24044          */
24045         beforepush: true,
24046          /**
24047          * @event sync
24048          * Fires when the textarea is updated with content from the editor iframe.
24049          * @param {Roo.HtmlEditorCore} this
24050          * @param {String} html
24051          */
24052         sync: true,
24053          /**
24054          * @event push
24055          * Fires when the iframe editor is updated with content from the textarea.
24056          * @param {Roo.HtmlEditorCore} this
24057          * @param {String} html
24058          */
24059         push: true,
24060         
24061         /**
24062          * @event editorevent
24063          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24064          * @param {Roo.HtmlEditorCore} this
24065          */
24066         editorevent: true
24067         
24068     });
24069     
24070     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24071     
24072     // defaults : white / black...
24073     this.applyBlacklists();
24074     
24075     
24076     
24077 };
24078
24079
24080 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24081
24082
24083      /**
24084      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24085      */
24086     
24087     owner : false,
24088     
24089      /**
24090      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24091      *                        Roo.resizable.
24092      */
24093     resizable : false,
24094      /**
24095      * @cfg {Number} height (in pixels)
24096      */   
24097     height: 300,
24098    /**
24099      * @cfg {Number} width (in pixels)
24100      */   
24101     width: 500,
24102     
24103     /**
24104      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24105      * 
24106      */
24107     stylesheets: false,
24108     
24109     // id of frame..
24110     frameId: false,
24111     
24112     // private properties
24113     validationEvent : false,
24114     deferHeight: true,
24115     initialized : false,
24116     activated : false,
24117     sourceEditMode : false,
24118     onFocus : Roo.emptyFn,
24119     iframePad:3,
24120     hideMode:'offsets',
24121     
24122     clearUp: true,
24123     
24124     // blacklist + whitelisted elements..
24125     black: false,
24126     white: false,
24127      
24128     bodyCls : '',
24129
24130     /**
24131      * Protected method that will not generally be called directly. It
24132      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24133      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24134      */
24135     getDocMarkup : function(){
24136         // body styles..
24137         var st = '';
24138         
24139         // inherit styels from page...?? 
24140         if (this.stylesheets === false) {
24141             
24142             Roo.get(document.head).select('style').each(function(node) {
24143                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24144             });
24145             
24146             Roo.get(document.head).select('link').each(function(node) { 
24147                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24148             });
24149             
24150         } else if (!this.stylesheets.length) {
24151                 // simple..
24152                 st = '<style type="text/css">' +
24153                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24154                    '</style>';
24155         } else {
24156             for (var i in this.stylesheets) { 
24157                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24158             }
24159             
24160         }
24161         
24162         st +=  '<style type="text/css">' +
24163             'IMG { cursor: pointer } ' +
24164         '</style>';
24165
24166         var cls = 'roo-htmleditor-body';
24167         
24168         if(this.bodyCls.length){
24169             cls += ' ' + this.bodyCls;
24170         }
24171         
24172         return '<html><head>' + st  +
24173             //<style type="text/css">' +
24174             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24175             //'</style>' +
24176             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24177     },
24178
24179     // private
24180     onRender : function(ct, position)
24181     {
24182         var _t = this;
24183         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24184         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24185         
24186         
24187         this.el.dom.style.border = '0 none';
24188         this.el.dom.setAttribute('tabIndex', -1);
24189         this.el.addClass('x-hidden hide');
24190         
24191         
24192         
24193         if(Roo.isIE){ // fix IE 1px bogus margin
24194             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24195         }
24196        
24197         
24198         this.frameId = Roo.id();
24199         
24200          
24201         
24202         var iframe = this.owner.wrap.createChild({
24203             tag: 'iframe',
24204             cls: 'form-control', // bootstrap..
24205             id: this.frameId,
24206             name: this.frameId,
24207             frameBorder : 'no',
24208             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24209         }, this.el
24210         );
24211         
24212         
24213         this.iframe = iframe.dom;
24214
24215          this.assignDocWin();
24216         
24217         this.doc.designMode = 'on';
24218        
24219         this.doc.open();
24220         this.doc.write(this.getDocMarkup());
24221         this.doc.close();
24222
24223         
24224         var task = { // must defer to wait for browser to be ready
24225             run : function(){
24226                 //console.log("run task?" + this.doc.readyState);
24227                 this.assignDocWin();
24228                 if(this.doc.body || this.doc.readyState == 'complete'){
24229                     try {
24230                         this.doc.designMode="on";
24231                     } catch (e) {
24232                         return;
24233                     }
24234                     Roo.TaskMgr.stop(task);
24235                     this.initEditor.defer(10, this);
24236                 }
24237             },
24238             interval : 10,
24239             duration: 10000,
24240             scope: this
24241         };
24242         Roo.TaskMgr.start(task);
24243
24244     },
24245
24246     // private
24247     onResize : function(w, h)
24248     {
24249          Roo.log('resize: ' +w + ',' + h );
24250         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24251         if(!this.iframe){
24252             return;
24253         }
24254         if(typeof w == 'number'){
24255             
24256             this.iframe.style.width = w + 'px';
24257         }
24258         if(typeof h == 'number'){
24259             
24260             this.iframe.style.height = h + 'px';
24261             if(this.doc){
24262                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24263             }
24264         }
24265         
24266     },
24267
24268     /**
24269      * Toggles the editor between standard and source edit mode.
24270      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24271      */
24272     toggleSourceEdit : function(sourceEditMode){
24273         
24274         this.sourceEditMode = sourceEditMode === true;
24275         
24276         if(this.sourceEditMode){
24277  
24278             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24279             
24280         }else{
24281             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24282             //this.iframe.className = '';
24283             this.deferFocus();
24284         }
24285         //this.setSize(this.owner.wrap.getSize());
24286         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24287     },
24288
24289     
24290   
24291
24292     /**
24293      * Protected method that will not generally be called directly. If you need/want
24294      * custom HTML cleanup, this is the method you should override.
24295      * @param {String} html The HTML to be cleaned
24296      * return {String} The cleaned HTML
24297      */
24298     cleanHtml : function(html){
24299         html = String(html);
24300         if(html.length > 5){
24301             if(Roo.isSafari){ // strip safari nonsense
24302                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24303             }
24304         }
24305         if(html == '&nbsp;'){
24306             html = '';
24307         }
24308         return html;
24309     },
24310
24311     /**
24312      * HTML Editor -> Textarea
24313      * Protected method that will not generally be called directly. Syncs the contents
24314      * of the editor iframe with the textarea.
24315      */
24316     syncValue : function(){
24317         if(this.initialized){
24318             var bd = (this.doc.body || this.doc.documentElement);
24319             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24320             var html = bd.innerHTML;
24321             if(Roo.isSafari){
24322                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24323                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24324                 if(m && m[1]){
24325                     html = '<div style="'+m[0]+'">' + html + '</div>';
24326                 }
24327             }
24328             html = this.cleanHtml(html);
24329             // fix up the special chars.. normaly like back quotes in word...
24330             // however we do not want to do this with chinese..
24331             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24332                 
24333                 var cc = match.charCodeAt();
24334
24335                 // Get the character value, handling surrogate pairs
24336                 if (match.length == 2) {
24337                     // It's a surrogate pair, calculate the Unicode code point
24338                     var high = match.charCodeAt(0) - 0xD800;
24339                     var low  = match.charCodeAt(1) - 0xDC00;
24340                     cc = (high * 0x400) + low + 0x10000;
24341                 }  else if (
24342                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24343                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24344                     (cc >= 0xf900 && cc < 0xfb00 )
24345                 ) {
24346                         return match;
24347                 }  
24348          
24349                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24350                 return "&#" + cc + ";";
24351                 
24352                 
24353             });
24354             
24355             
24356              
24357             if(this.owner.fireEvent('beforesync', this, html) !== false){
24358                 this.el.dom.value = html;
24359                 this.owner.fireEvent('sync', this, html);
24360             }
24361         }
24362     },
24363
24364     /**
24365      * Protected method that will not generally be called directly. Pushes the value of the textarea
24366      * into the iframe editor.
24367      */
24368     pushValue : function(){
24369         if(this.initialized){
24370             var v = this.el.dom.value.trim();
24371             
24372 //            if(v.length < 1){
24373 //                v = '&#160;';
24374 //            }
24375             
24376             if(this.owner.fireEvent('beforepush', this, v) !== false){
24377                 var d = (this.doc.body || this.doc.documentElement);
24378                 d.innerHTML = v;
24379                 this.cleanUpPaste();
24380                 this.el.dom.value = d.innerHTML;
24381                 this.owner.fireEvent('push', this, v);
24382             }
24383         }
24384     },
24385
24386     // private
24387     deferFocus : function(){
24388         this.focus.defer(10, this);
24389     },
24390
24391     // doc'ed in Field
24392     focus : function(){
24393         if(this.win && !this.sourceEditMode){
24394             this.win.focus();
24395         }else{
24396             this.el.focus();
24397         }
24398     },
24399     
24400     assignDocWin: function()
24401     {
24402         var iframe = this.iframe;
24403         
24404          if(Roo.isIE){
24405             this.doc = iframe.contentWindow.document;
24406             this.win = iframe.contentWindow;
24407         } else {
24408 //            if (!Roo.get(this.frameId)) {
24409 //                return;
24410 //            }
24411 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24412 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24413             
24414             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24415                 return;
24416             }
24417             
24418             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24419             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24420         }
24421     },
24422     
24423     // private
24424     initEditor : function(){
24425         //console.log("INIT EDITOR");
24426         this.assignDocWin();
24427         
24428         
24429         
24430         this.doc.designMode="on";
24431         this.doc.open();
24432         this.doc.write(this.getDocMarkup());
24433         this.doc.close();
24434         
24435         var dbody = (this.doc.body || this.doc.documentElement);
24436         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24437         // this copies styles from the containing element into thsi one..
24438         // not sure why we need all of this..
24439         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24440         
24441         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24442         //ss['background-attachment'] = 'fixed'; // w3c
24443         dbody.bgProperties = 'fixed'; // ie
24444         //Roo.DomHelper.applyStyles(dbody, ss);
24445         Roo.EventManager.on(this.doc, {
24446             //'mousedown': this.onEditorEvent,
24447             'mouseup': this.onEditorEvent,
24448             'dblclick': this.onEditorEvent,
24449             'click': this.onEditorEvent,
24450             'keyup': this.onEditorEvent,
24451             buffer:100,
24452             scope: this
24453         });
24454         if(Roo.isGecko){
24455             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24456         }
24457         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24458             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24459         }
24460         this.initialized = true;
24461
24462         this.owner.fireEvent('initialize', this);
24463         this.pushValue();
24464     },
24465
24466     // private
24467     onDestroy : function(){
24468         
24469         
24470         
24471         if(this.rendered){
24472             
24473             //for (var i =0; i < this.toolbars.length;i++) {
24474             //    // fixme - ask toolbars for heights?
24475             //    this.toolbars[i].onDestroy();
24476            // }
24477             
24478             //this.wrap.dom.innerHTML = '';
24479             //this.wrap.remove();
24480         }
24481     },
24482
24483     // private
24484     onFirstFocus : function(){
24485         
24486         this.assignDocWin();
24487         
24488         
24489         this.activated = true;
24490          
24491     
24492         if(Roo.isGecko){ // prevent silly gecko errors
24493             this.win.focus();
24494             var s = this.win.getSelection();
24495             if(!s.focusNode || s.focusNode.nodeType != 3){
24496                 var r = s.getRangeAt(0);
24497                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24498                 r.collapse(true);
24499                 this.deferFocus();
24500             }
24501             try{
24502                 this.execCmd('useCSS', true);
24503                 this.execCmd('styleWithCSS', false);
24504             }catch(e){}
24505         }
24506         this.owner.fireEvent('activate', this);
24507     },
24508
24509     // private
24510     adjustFont: function(btn){
24511         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24512         //if(Roo.isSafari){ // safari
24513         //    adjust *= 2;
24514        // }
24515         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24516         if(Roo.isSafari){ // safari
24517             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24518             v =  (v < 10) ? 10 : v;
24519             v =  (v > 48) ? 48 : v;
24520             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24521             
24522         }
24523         
24524         
24525         v = Math.max(1, v+adjust);
24526         
24527         this.execCmd('FontSize', v  );
24528     },
24529
24530     onEditorEvent : function(e)
24531     {
24532         this.owner.fireEvent('editorevent', this, e);
24533       //  this.updateToolbar();
24534         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24535     },
24536
24537     insertTag : function(tg)
24538     {
24539         // could be a bit smarter... -> wrap the current selected tRoo..
24540         if (tg.toLowerCase() == 'span' ||
24541             tg.toLowerCase() == 'code' ||
24542             tg.toLowerCase() == 'sup' ||
24543             tg.toLowerCase() == 'sub' 
24544             ) {
24545             
24546             range = this.createRange(this.getSelection());
24547             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24548             wrappingNode.appendChild(range.extractContents());
24549             range.insertNode(wrappingNode);
24550
24551             return;
24552             
24553             
24554             
24555         }
24556         this.execCmd("formatblock",   tg);
24557         
24558     },
24559     
24560     insertText : function(txt)
24561     {
24562         
24563         
24564         var range = this.createRange();
24565         range.deleteContents();
24566                //alert(Sender.getAttribute('label'));
24567                
24568         range.insertNode(this.doc.createTextNode(txt));
24569     } ,
24570     
24571      
24572
24573     /**
24574      * Executes a Midas editor command on the editor document and performs necessary focus and
24575      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24576      * @param {String} cmd The Midas command
24577      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24578      */
24579     relayCmd : function(cmd, value){
24580         this.win.focus();
24581         this.execCmd(cmd, value);
24582         this.owner.fireEvent('editorevent', this);
24583         //this.updateToolbar();
24584         this.owner.deferFocus();
24585     },
24586
24587     /**
24588      * Executes a Midas editor command directly on the editor document.
24589      * For visual commands, you should use {@link #relayCmd} instead.
24590      * <b>This should only be called after the editor is initialized.</b>
24591      * @param {String} cmd The Midas command
24592      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24593      */
24594     execCmd : function(cmd, value){
24595         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24596         this.syncValue();
24597     },
24598  
24599  
24600    
24601     /**
24602      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24603      * to insert tRoo.
24604      * @param {String} text | dom node.. 
24605      */
24606     insertAtCursor : function(text)
24607     {
24608         
24609         if(!this.activated){
24610             return;
24611         }
24612         /*
24613         if(Roo.isIE){
24614             this.win.focus();
24615             var r = this.doc.selection.createRange();
24616             if(r){
24617                 r.collapse(true);
24618                 r.pasteHTML(text);
24619                 this.syncValue();
24620                 this.deferFocus();
24621             
24622             }
24623             return;
24624         }
24625         */
24626         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24627             this.win.focus();
24628             
24629             
24630             // from jquery ui (MIT licenced)
24631             var range, node;
24632             var win = this.win;
24633             
24634             if (win.getSelection && win.getSelection().getRangeAt) {
24635                 range = win.getSelection().getRangeAt(0);
24636                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24637                 range.insertNode(node);
24638             } else if (win.document.selection && win.document.selection.createRange) {
24639                 // no firefox support
24640                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24641                 win.document.selection.createRange().pasteHTML(txt);
24642             } else {
24643                 // no firefox support
24644                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24645                 this.execCmd('InsertHTML', txt);
24646             } 
24647             
24648             this.syncValue();
24649             
24650             this.deferFocus();
24651         }
24652     },
24653  // private
24654     mozKeyPress : function(e){
24655         if(e.ctrlKey){
24656             var c = e.getCharCode(), cmd;
24657           
24658             if(c > 0){
24659                 c = String.fromCharCode(c).toLowerCase();
24660                 switch(c){
24661                     case 'b':
24662                         cmd = 'bold';
24663                         break;
24664                     case 'i':
24665                         cmd = 'italic';
24666                         break;
24667                     
24668                     case 'u':
24669                         cmd = 'underline';
24670                         break;
24671                     
24672                     case 'v':
24673                         this.cleanUpPaste.defer(100, this);
24674                         return;
24675                         
24676                 }
24677                 if(cmd){
24678                     this.win.focus();
24679                     this.execCmd(cmd);
24680                     this.deferFocus();
24681                     e.preventDefault();
24682                 }
24683                 
24684             }
24685         }
24686     },
24687
24688     // private
24689     fixKeys : function(){ // load time branching for fastest keydown performance
24690         if(Roo.isIE){
24691             return function(e){
24692                 var k = e.getKey(), r;
24693                 if(k == e.TAB){
24694                     e.stopEvent();
24695                     r = this.doc.selection.createRange();
24696                     if(r){
24697                         r.collapse(true);
24698                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24699                         this.deferFocus();
24700                     }
24701                     return;
24702                 }
24703                 
24704                 if(k == e.ENTER){
24705                     r = this.doc.selection.createRange();
24706                     if(r){
24707                         var target = r.parentElement();
24708                         if(!target || target.tagName.toLowerCase() != 'li'){
24709                             e.stopEvent();
24710                             r.pasteHTML('<br />');
24711                             r.collapse(false);
24712                             r.select();
24713                         }
24714                     }
24715                 }
24716                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24717                     this.cleanUpPaste.defer(100, this);
24718                     return;
24719                 }
24720                 
24721                 
24722             };
24723         }else if(Roo.isOpera){
24724             return function(e){
24725                 var k = e.getKey();
24726                 if(k == e.TAB){
24727                     e.stopEvent();
24728                     this.win.focus();
24729                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24730                     this.deferFocus();
24731                 }
24732                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24733                     this.cleanUpPaste.defer(100, this);
24734                     return;
24735                 }
24736                 
24737             };
24738         }else if(Roo.isSafari){
24739             return function(e){
24740                 var k = e.getKey();
24741                 
24742                 if(k == e.TAB){
24743                     e.stopEvent();
24744                     this.execCmd('InsertText','\t');
24745                     this.deferFocus();
24746                     return;
24747                 }
24748                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24749                     this.cleanUpPaste.defer(100, this);
24750                     return;
24751                 }
24752                 
24753              };
24754         }
24755     }(),
24756     
24757     getAllAncestors: function()
24758     {
24759         var p = this.getSelectedNode();
24760         var a = [];
24761         if (!p) {
24762             a.push(p); // push blank onto stack..
24763             p = this.getParentElement();
24764         }
24765         
24766         
24767         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24768             a.push(p);
24769             p = p.parentNode;
24770         }
24771         a.push(this.doc.body);
24772         return a;
24773     },
24774     lastSel : false,
24775     lastSelNode : false,
24776     
24777     
24778     getSelection : function() 
24779     {
24780         this.assignDocWin();
24781         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24782     },
24783     
24784     getSelectedNode: function() 
24785     {
24786         // this may only work on Gecko!!!
24787         
24788         // should we cache this!!!!
24789         
24790         
24791         
24792          
24793         var range = this.createRange(this.getSelection()).cloneRange();
24794         
24795         if (Roo.isIE) {
24796             var parent = range.parentElement();
24797             while (true) {
24798                 var testRange = range.duplicate();
24799                 testRange.moveToElementText(parent);
24800                 if (testRange.inRange(range)) {
24801                     break;
24802                 }
24803                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24804                     break;
24805                 }
24806                 parent = parent.parentElement;
24807             }
24808             return parent;
24809         }
24810         
24811         // is ancestor a text element.
24812         var ac =  range.commonAncestorContainer;
24813         if (ac.nodeType == 3) {
24814             ac = ac.parentNode;
24815         }
24816         
24817         var ar = ac.childNodes;
24818          
24819         var nodes = [];
24820         var other_nodes = [];
24821         var has_other_nodes = false;
24822         for (var i=0;i<ar.length;i++) {
24823             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24824                 continue;
24825             }
24826             // fullly contained node.
24827             
24828             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24829                 nodes.push(ar[i]);
24830                 continue;
24831             }
24832             
24833             // probably selected..
24834             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24835                 other_nodes.push(ar[i]);
24836                 continue;
24837             }
24838             // outer..
24839             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24840                 continue;
24841             }
24842             
24843             
24844             has_other_nodes = true;
24845         }
24846         if (!nodes.length && other_nodes.length) {
24847             nodes= other_nodes;
24848         }
24849         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24850             return false;
24851         }
24852         
24853         return nodes[0];
24854     },
24855     createRange: function(sel)
24856     {
24857         // this has strange effects when using with 
24858         // top toolbar - not sure if it's a great idea.
24859         //this.editor.contentWindow.focus();
24860         if (typeof sel != "undefined") {
24861             try {
24862                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24863             } catch(e) {
24864                 return this.doc.createRange();
24865             }
24866         } else {
24867             return this.doc.createRange();
24868         }
24869     },
24870     getParentElement: function()
24871     {
24872         
24873         this.assignDocWin();
24874         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24875         
24876         var range = this.createRange(sel);
24877          
24878         try {
24879             var p = range.commonAncestorContainer;
24880             while (p.nodeType == 3) { // text node
24881                 p = p.parentNode;
24882             }
24883             return p;
24884         } catch (e) {
24885             return null;
24886         }
24887     
24888     },
24889     /***
24890      *
24891      * Range intersection.. the hard stuff...
24892      *  '-1' = before
24893      *  '0' = hits..
24894      *  '1' = after.
24895      *         [ -- selected range --- ]
24896      *   [fail]                        [fail]
24897      *
24898      *    basically..
24899      *      if end is before start or  hits it. fail.
24900      *      if start is after end or hits it fail.
24901      *
24902      *   if either hits (but other is outside. - then it's not 
24903      *   
24904      *    
24905      **/
24906     
24907     
24908     // @see http://www.thismuchiknow.co.uk/?p=64.
24909     rangeIntersectsNode : function(range, node)
24910     {
24911         var nodeRange = node.ownerDocument.createRange();
24912         try {
24913             nodeRange.selectNode(node);
24914         } catch (e) {
24915             nodeRange.selectNodeContents(node);
24916         }
24917     
24918         var rangeStartRange = range.cloneRange();
24919         rangeStartRange.collapse(true);
24920     
24921         var rangeEndRange = range.cloneRange();
24922         rangeEndRange.collapse(false);
24923     
24924         var nodeStartRange = nodeRange.cloneRange();
24925         nodeStartRange.collapse(true);
24926     
24927         var nodeEndRange = nodeRange.cloneRange();
24928         nodeEndRange.collapse(false);
24929     
24930         return rangeStartRange.compareBoundaryPoints(
24931                  Range.START_TO_START, nodeEndRange) == -1 &&
24932                rangeEndRange.compareBoundaryPoints(
24933                  Range.START_TO_START, nodeStartRange) == 1;
24934         
24935          
24936     },
24937     rangeCompareNode : function(range, node)
24938     {
24939         var nodeRange = node.ownerDocument.createRange();
24940         try {
24941             nodeRange.selectNode(node);
24942         } catch (e) {
24943             nodeRange.selectNodeContents(node);
24944         }
24945         
24946         
24947         range.collapse(true);
24948     
24949         nodeRange.collapse(true);
24950      
24951         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24952         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24953          
24954         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24955         
24956         var nodeIsBefore   =  ss == 1;
24957         var nodeIsAfter    = ee == -1;
24958         
24959         if (nodeIsBefore && nodeIsAfter) {
24960             return 0; // outer
24961         }
24962         if (!nodeIsBefore && nodeIsAfter) {
24963             return 1; //right trailed.
24964         }
24965         
24966         if (nodeIsBefore && !nodeIsAfter) {
24967             return 2;  // left trailed.
24968         }
24969         // fully contined.
24970         return 3;
24971     },
24972
24973     // private? - in a new class?
24974     cleanUpPaste :  function()
24975     {
24976         // cleans up the whole document..
24977         Roo.log('cleanuppaste');
24978         
24979         this.cleanUpChildren(this.doc.body);
24980         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24981         if (clean != this.doc.body.innerHTML) {
24982             this.doc.body.innerHTML = clean;
24983         }
24984         
24985     },
24986     
24987     cleanWordChars : function(input) {// change the chars to hex code
24988         var he = Roo.HtmlEditorCore;
24989         
24990         var output = input;
24991         Roo.each(he.swapCodes, function(sw) { 
24992             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24993             
24994             output = output.replace(swapper, sw[1]);
24995         });
24996         
24997         return output;
24998     },
24999     
25000     
25001     cleanUpChildren : function (n)
25002     {
25003         if (!n.childNodes.length) {
25004             return;
25005         }
25006         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25007            this.cleanUpChild(n.childNodes[i]);
25008         }
25009     },
25010     
25011     
25012         
25013     
25014     cleanUpChild : function (node)
25015     {
25016         var ed = this;
25017         //console.log(node);
25018         if (node.nodeName == "#text") {
25019             // clean up silly Windows -- stuff?
25020             return; 
25021         }
25022         if (node.nodeName == "#comment") {
25023             node.parentNode.removeChild(node);
25024             // clean up silly Windows -- stuff?
25025             return; 
25026         }
25027         var lcname = node.tagName.toLowerCase();
25028         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25029         // whitelist of tags..
25030         
25031         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25032             // remove node.
25033             node.parentNode.removeChild(node);
25034             return;
25035             
25036         }
25037         
25038         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25039         
25040         // spans with no attributes - just remove them..
25041         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25042             remove_keep_children = true;
25043         }
25044         
25045         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25046         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25047         
25048         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25049         //    remove_keep_children = true;
25050         //}
25051         
25052         if (remove_keep_children) {
25053             this.cleanUpChildren(node);
25054             // inserts everything just before this node...
25055             while (node.childNodes.length) {
25056                 var cn = node.childNodes[0];
25057                 node.removeChild(cn);
25058                 node.parentNode.insertBefore(cn, node);
25059             }
25060             node.parentNode.removeChild(node);
25061             return;
25062         }
25063         
25064         if (!node.attributes || !node.attributes.length) {
25065             
25066           
25067             
25068             
25069             this.cleanUpChildren(node);
25070             return;
25071         }
25072         
25073         function cleanAttr(n,v)
25074         {
25075             
25076             if (v.match(/^\./) || v.match(/^\//)) {
25077                 return;
25078             }
25079             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25080                 return;
25081             }
25082             if (v.match(/^#/)) {
25083                 return;
25084             }
25085             if (v.match(/^\{/)) { // allow template editing.
25086                 return;
25087             }
25088 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25089             node.removeAttribute(n);
25090             
25091         }
25092         
25093         var cwhite = this.cwhite;
25094         var cblack = this.cblack;
25095             
25096         function cleanStyle(n,v)
25097         {
25098             if (v.match(/expression/)) { //XSS?? should we even bother..
25099                 node.removeAttribute(n);
25100                 return;
25101             }
25102             
25103             var parts = v.split(/;/);
25104             var clean = [];
25105             
25106             Roo.each(parts, function(p) {
25107                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25108                 if (!p.length) {
25109                     return true;
25110                 }
25111                 var l = p.split(':').shift().replace(/\s+/g,'');
25112                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25113                 
25114                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25115 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25116                     //node.removeAttribute(n);
25117                     return true;
25118                 }
25119                 //Roo.log()
25120                 // only allow 'c whitelisted system attributes'
25121                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25122 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25123                     //node.removeAttribute(n);
25124                     return true;
25125                 }
25126                 
25127                 
25128                  
25129                 
25130                 clean.push(p);
25131                 return true;
25132             });
25133             if (clean.length) { 
25134                 node.setAttribute(n, clean.join(';'));
25135             } else {
25136                 node.removeAttribute(n);
25137             }
25138             
25139         }
25140         
25141         
25142         for (var i = node.attributes.length-1; i > -1 ; i--) {
25143             var a = node.attributes[i];
25144             //console.log(a);
25145             
25146             if (a.name.toLowerCase().substr(0,2)=='on')  {
25147                 node.removeAttribute(a.name);
25148                 continue;
25149             }
25150             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25151                 node.removeAttribute(a.name);
25152                 continue;
25153             }
25154             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25155                 cleanAttr(a.name,a.value); // fixme..
25156                 continue;
25157             }
25158             if (a.name == 'style') {
25159                 cleanStyle(a.name,a.value);
25160                 continue;
25161             }
25162             /// clean up MS crap..
25163             // tecnically this should be a list of valid class'es..
25164             
25165             
25166             if (a.name == 'class') {
25167                 if (a.value.match(/^Mso/)) {
25168                     node.removeAttribute('class');
25169                 }
25170                 
25171                 if (a.value.match(/^body$/)) {
25172                     node.removeAttribute('class');
25173                 }
25174                 continue;
25175             }
25176             
25177             // style cleanup!?
25178             // class cleanup?
25179             
25180         }
25181         
25182         
25183         this.cleanUpChildren(node);
25184         
25185         
25186     },
25187     
25188     /**
25189      * Clean up MS wordisms...
25190      */
25191     cleanWord : function(node)
25192     {
25193         if (!node) {
25194             this.cleanWord(this.doc.body);
25195             return;
25196         }
25197         
25198         if(
25199                 node.nodeName == 'SPAN' &&
25200                 !node.hasAttributes() &&
25201                 node.childNodes.length == 1 &&
25202                 node.firstChild.nodeName == "#text"  
25203         ) {
25204             var textNode = node.firstChild;
25205             node.removeChild(textNode);
25206             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25207                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25208             }
25209             node.parentNode.insertBefore(textNode, node);
25210             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25211                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25212             }
25213             node.parentNode.removeChild(node);
25214         }
25215         
25216         if (node.nodeName == "#text") {
25217             // clean up silly Windows -- stuff?
25218             return; 
25219         }
25220         if (node.nodeName == "#comment") {
25221             node.parentNode.removeChild(node);
25222             // clean up silly Windows -- stuff?
25223             return; 
25224         }
25225         
25226         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25227             node.parentNode.removeChild(node);
25228             return;
25229         }
25230         //Roo.log(node.tagName);
25231         // remove - but keep children..
25232         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25233             //Roo.log('-- removed');
25234             while (node.childNodes.length) {
25235                 var cn = node.childNodes[0];
25236                 node.removeChild(cn);
25237                 node.parentNode.insertBefore(cn, node);
25238                 // move node to parent - and clean it..
25239                 this.cleanWord(cn);
25240             }
25241             node.parentNode.removeChild(node);
25242             /// no need to iterate chidlren = it's got none..
25243             //this.iterateChildren(node, this.cleanWord);
25244             return;
25245         }
25246         // clean styles
25247         if (node.className.length) {
25248             
25249             var cn = node.className.split(/\W+/);
25250             var cna = [];
25251             Roo.each(cn, function(cls) {
25252                 if (cls.match(/Mso[a-zA-Z]+/)) {
25253                     return;
25254                 }
25255                 cna.push(cls);
25256             });
25257             node.className = cna.length ? cna.join(' ') : '';
25258             if (!cna.length) {
25259                 node.removeAttribute("class");
25260             }
25261         }
25262         
25263         if (node.hasAttribute("lang")) {
25264             node.removeAttribute("lang");
25265         }
25266         
25267         if (node.hasAttribute("style")) {
25268             
25269             var styles = node.getAttribute("style").split(";");
25270             var nstyle = [];
25271             Roo.each(styles, function(s) {
25272                 if (!s.match(/:/)) {
25273                     return;
25274                 }
25275                 var kv = s.split(":");
25276                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25277                     return;
25278                 }
25279                 // what ever is left... we allow.
25280                 nstyle.push(s);
25281             });
25282             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25283             if (!nstyle.length) {
25284                 node.removeAttribute('style');
25285             }
25286         }
25287         this.iterateChildren(node, this.cleanWord);
25288         
25289         
25290         
25291     },
25292     /**
25293      * iterateChildren of a Node, calling fn each time, using this as the scole..
25294      * @param {DomNode} node node to iterate children of.
25295      * @param {Function} fn method of this class to call on each item.
25296      */
25297     iterateChildren : function(node, fn)
25298     {
25299         if (!node.childNodes.length) {
25300                 return;
25301         }
25302         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25303            fn.call(this, node.childNodes[i])
25304         }
25305     },
25306     
25307     
25308     /**
25309      * cleanTableWidths.
25310      *
25311      * Quite often pasting from word etc.. results in tables with column and widths.
25312      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25313      *
25314      */
25315     cleanTableWidths : function(node)
25316     {
25317          
25318          
25319         if (!node) {
25320             this.cleanTableWidths(this.doc.body);
25321             return;
25322         }
25323         
25324         // ignore list...
25325         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25326             return; 
25327         }
25328         Roo.log(node.tagName);
25329         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25330             this.iterateChildren(node, this.cleanTableWidths);
25331             return;
25332         }
25333         if (node.hasAttribute('width')) {
25334             node.removeAttribute('width');
25335         }
25336         
25337          
25338         if (node.hasAttribute("style")) {
25339             // pretty basic...
25340             
25341             var styles = node.getAttribute("style").split(";");
25342             var nstyle = [];
25343             Roo.each(styles, function(s) {
25344                 if (!s.match(/:/)) {
25345                     return;
25346                 }
25347                 var kv = s.split(":");
25348                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25349                     return;
25350                 }
25351                 // what ever is left... we allow.
25352                 nstyle.push(s);
25353             });
25354             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25355             if (!nstyle.length) {
25356                 node.removeAttribute('style');
25357             }
25358         }
25359         
25360         this.iterateChildren(node, this.cleanTableWidths);
25361         
25362         
25363     },
25364     
25365     
25366     
25367     
25368     domToHTML : function(currentElement, depth, nopadtext) {
25369         
25370         depth = depth || 0;
25371         nopadtext = nopadtext || false;
25372     
25373         if (!currentElement) {
25374             return this.domToHTML(this.doc.body);
25375         }
25376         
25377         //Roo.log(currentElement);
25378         var j;
25379         var allText = false;
25380         var nodeName = currentElement.nodeName;
25381         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25382         
25383         if  (nodeName == '#text') {
25384             
25385             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25386         }
25387         
25388         
25389         var ret = '';
25390         if (nodeName != 'BODY') {
25391              
25392             var i = 0;
25393             // Prints the node tagName, such as <A>, <IMG>, etc
25394             if (tagName) {
25395                 var attr = [];
25396                 for(i = 0; i < currentElement.attributes.length;i++) {
25397                     // quoting?
25398                     var aname = currentElement.attributes.item(i).name;
25399                     if (!currentElement.attributes.item(i).value.length) {
25400                         continue;
25401                     }
25402                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25403                 }
25404                 
25405                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25406             } 
25407             else {
25408                 
25409                 // eack
25410             }
25411         } else {
25412             tagName = false;
25413         }
25414         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25415             return ret;
25416         }
25417         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25418             nopadtext = true;
25419         }
25420         
25421         
25422         // Traverse the tree
25423         i = 0;
25424         var currentElementChild = currentElement.childNodes.item(i);
25425         var allText = true;
25426         var innerHTML  = '';
25427         lastnode = '';
25428         while (currentElementChild) {
25429             // Formatting code (indent the tree so it looks nice on the screen)
25430             var nopad = nopadtext;
25431             if (lastnode == 'SPAN') {
25432                 nopad  = true;
25433             }
25434             // text
25435             if  (currentElementChild.nodeName == '#text') {
25436                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25437                 toadd = nopadtext ? toadd : toadd.trim();
25438                 if (!nopad && toadd.length > 80) {
25439                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25440                 }
25441                 innerHTML  += toadd;
25442                 
25443                 i++;
25444                 currentElementChild = currentElement.childNodes.item(i);
25445                 lastNode = '';
25446                 continue;
25447             }
25448             allText = false;
25449             
25450             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25451                 
25452             // Recursively traverse the tree structure of the child node
25453             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25454             lastnode = currentElementChild.nodeName;
25455             i++;
25456             currentElementChild=currentElement.childNodes.item(i);
25457         }
25458         
25459         ret += innerHTML;
25460         
25461         if (!allText) {
25462                 // The remaining code is mostly for formatting the tree
25463             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25464         }
25465         
25466         
25467         if (tagName) {
25468             ret+= "</"+tagName+">";
25469         }
25470         return ret;
25471         
25472     },
25473         
25474     applyBlacklists : function()
25475     {
25476         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25477         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25478         
25479         this.white = [];
25480         this.black = [];
25481         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25482             if (b.indexOf(tag) > -1) {
25483                 return;
25484             }
25485             this.white.push(tag);
25486             
25487         }, this);
25488         
25489         Roo.each(w, function(tag) {
25490             if (b.indexOf(tag) > -1) {
25491                 return;
25492             }
25493             if (this.white.indexOf(tag) > -1) {
25494                 return;
25495             }
25496             this.white.push(tag);
25497             
25498         }, this);
25499         
25500         
25501         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25502             if (w.indexOf(tag) > -1) {
25503                 return;
25504             }
25505             this.black.push(tag);
25506             
25507         }, this);
25508         
25509         Roo.each(b, function(tag) {
25510             if (w.indexOf(tag) > -1) {
25511                 return;
25512             }
25513             if (this.black.indexOf(tag) > -1) {
25514                 return;
25515             }
25516             this.black.push(tag);
25517             
25518         }, this);
25519         
25520         
25521         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25522         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25523         
25524         this.cwhite = [];
25525         this.cblack = [];
25526         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25527             if (b.indexOf(tag) > -1) {
25528                 return;
25529             }
25530             this.cwhite.push(tag);
25531             
25532         }, this);
25533         
25534         Roo.each(w, function(tag) {
25535             if (b.indexOf(tag) > -1) {
25536                 return;
25537             }
25538             if (this.cwhite.indexOf(tag) > -1) {
25539                 return;
25540             }
25541             this.cwhite.push(tag);
25542             
25543         }, this);
25544         
25545         
25546         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25547             if (w.indexOf(tag) > -1) {
25548                 return;
25549             }
25550             this.cblack.push(tag);
25551             
25552         }, this);
25553         
25554         Roo.each(b, function(tag) {
25555             if (w.indexOf(tag) > -1) {
25556                 return;
25557             }
25558             if (this.cblack.indexOf(tag) > -1) {
25559                 return;
25560             }
25561             this.cblack.push(tag);
25562             
25563         }, this);
25564     },
25565     
25566     setStylesheets : function(stylesheets)
25567     {
25568         if(typeof(stylesheets) == 'string'){
25569             Roo.get(this.iframe.contentDocument.head).createChild({
25570                 tag : 'link',
25571                 rel : 'stylesheet',
25572                 type : 'text/css',
25573                 href : stylesheets
25574             });
25575             
25576             return;
25577         }
25578         var _this = this;
25579      
25580         Roo.each(stylesheets, function(s) {
25581             if(!s.length){
25582                 return;
25583             }
25584             
25585             Roo.get(_this.iframe.contentDocument.head).createChild({
25586                 tag : 'link',
25587                 rel : 'stylesheet',
25588                 type : 'text/css',
25589                 href : s
25590             });
25591         });
25592
25593         
25594     },
25595     
25596     removeStylesheets : function()
25597     {
25598         var _this = this;
25599         
25600         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25601             s.remove();
25602         });
25603     },
25604     
25605     setStyle : function(style)
25606     {
25607         Roo.get(this.iframe.contentDocument.head).createChild({
25608             tag : 'style',
25609             type : 'text/css',
25610             html : style
25611         });
25612
25613         return;
25614     }
25615     
25616     // hide stuff that is not compatible
25617     /**
25618      * @event blur
25619      * @hide
25620      */
25621     /**
25622      * @event change
25623      * @hide
25624      */
25625     /**
25626      * @event focus
25627      * @hide
25628      */
25629     /**
25630      * @event specialkey
25631      * @hide
25632      */
25633     /**
25634      * @cfg {String} fieldClass @hide
25635      */
25636     /**
25637      * @cfg {String} focusClass @hide
25638      */
25639     /**
25640      * @cfg {String} autoCreate @hide
25641      */
25642     /**
25643      * @cfg {String} inputType @hide
25644      */
25645     /**
25646      * @cfg {String} invalidClass @hide
25647      */
25648     /**
25649      * @cfg {String} invalidText @hide
25650      */
25651     /**
25652      * @cfg {String} msgFx @hide
25653      */
25654     /**
25655      * @cfg {String} validateOnBlur @hide
25656      */
25657 });
25658
25659 Roo.HtmlEditorCore.white = [
25660         'area', 'br', 'img', 'input', 'hr', 'wbr',
25661         
25662        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25663        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25664        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25665        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25666        'table',   'ul',         'xmp', 
25667        
25668        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25669       'thead',   'tr', 
25670      
25671       'dir', 'menu', 'ol', 'ul', 'dl',
25672        
25673       'embed',  'object'
25674 ];
25675
25676
25677 Roo.HtmlEditorCore.black = [
25678     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25679         'applet', // 
25680         'base',   'basefont', 'bgsound', 'blink',  'body', 
25681         'frame',  'frameset', 'head',    'html',   'ilayer', 
25682         'iframe', 'layer',  'link',     'meta',    'object',   
25683         'script', 'style' ,'title',  'xml' // clean later..
25684 ];
25685 Roo.HtmlEditorCore.clean = [
25686     'script', 'style', 'title', 'xml'
25687 ];
25688 Roo.HtmlEditorCore.remove = [
25689     'font'
25690 ];
25691 // attributes..
25692
25693 Roo.HtmlEditorCore.ablack = [
25694     'on'
25695 ];
25696     
25697 Roo.HtmlEditorCore.aclean = [ 
25698     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25699 ];
25700
25701 // protocols..
25702 Roo.HtmlEditorCore.pwhite= [
25703         'http',  'https',  'mailto'
25704 ];
25705
25706 // white listed style attributes.
25707 Roo.HtmlEditorCore.cwhite= [
25708       //  'text-align', /// default is to allow most things..
25709       
25710          
25711 //        'font-size'//??
25712 ];
25713
25714 // black listed style attributes.
25715 Roo.HtmlEditorCore.cblack= [
25716       //  'font-size' -- this can be set by the project 
25717 ];
25718
25719
25720 Roo.HtmlEditorCore.swapCodes   =[ 
25721     [    8211, "--" ], 
25722     [    8212, "--" ], 
25723     [    8216,  "'" ],  
25724     [    8217, "'" ],  
25725     [    8220, '"' ],  
25726     [    8221, '"' ],  
25727     [    8226, "*" ],  
25728     [    8230, "..." ]
25729 ]; 
25730
25731     /*
25732  * - LGPL
25733  *
25734  * HtmlEditor
25735  * 
25736  */
25737
25738 /**
25739  * @class Roo.bootstrap.HtmlEditor
25740  * @extends Roo.bootstrap.TextArea
25741  * Bootstrap HtmlEditor class
25742
25743  * @constructor
25744  * Create a new HtmlEditor
25745  * @param {Object} config The config object
25746  */
25747
25748 Roo.bootstrap.HtmlEditor = function(config){
25749     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25750     if (!this.toolbars) {
25751         this.toolbars = [];
25752     }
25753     
25754     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25755     this.addEvents({
25756             /**
25757              * @event initialize
25758              * Fires when the editor is fully initialized (including the iframe)
25759              * @param {HtmlEditor} this
25760              */
25761             initialize: true,
25762             /**
25763              * @event activate
25764              * Fires when the editor is first receives the focus. Any insertion must wait
25765              * until after this event.
25766              * @param {HtmlEditor} this
25767              */
25768             activate: true,
25769              /**
25770              * @event beforesync
25771              * Fires before the textarea is updated with content from the editor iframe. Return false
25772              * to cancel the sync.
25773              * @param {HtmlEditor} this
25774              * @param {String} html
25775              */
25776             beforesync: true,
25777              /**
25778              * @event beforepush
25779              * Fires before the iframe editor is updated with content from the textarea. Return false
25780              * to cancel the push.
25781              * @param {HtmlEditor} this
25782              * @param {String} html
25783              */
25784             beforepush: true,
25785              /**
25786              * @event sync
25787              * Fires when the textarea is updated with content from the editor iframe.
25788              * @param {HtmlEditor} this
25789              * @param {String} html
25790              */
25791             sync: true,
25792              /**
25793              * @event push
25794              * Fires when the iframe editor is updated with content from the textarea.
25795              * @param {HtmlEditor} this
25796              * @param {String} html
25797              */
25798             push: true,
25799              /**
25800              * @event editmodechange
25801              * Fires when the editor switches edit modes
25802              * @param {HtmlEditor} this
25803              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25804              */
25805             editmodechange: true,
25806             /**
25807              * @event editorevent
25808              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25809              * @param {HtmlEditor} this
25810              */
25811             editorevent: true,
25812             /**
25813              * @event firstfocus
25814              * Fires when on first focus - needed by toolbars..
25815              * @param {HtmlEditor} this
25816              */
25817             firstfocus: true,
25818             /**
25819              * @event autosave
25820              * Auto save the htmlEditor value as a file into Events
25821              * @param {HtmlEditor} this
25822              */
25823             autosave: true,
25824             /**
25825              * @event savedpreview
25826              * preview the saved version of htmlEditor
25827              * @param {HtmlEditor} this
25828              */
25829             savedpreview: true
25830         });
25831 };
25832
25833
25834 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25835     
25836     
25837       /**
25838      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25839      */
25840     toolbars : false,
25841     
25842      /**
25843     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25844     */
25845     btns : [],
25846    
25847      /**
25848      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25849      *                        Roo.resizable.
25850      */
25851     resizable : false,
25852      /**
25853      * @cfg {Number} height (in pixels)
25854      */   
25855     height: 300,
25856    /**
25857      * @cfg {Number} width (in pixels)
25858      */   
25859     width: false,
25860     
25861     /**
25862      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25863      * 
25864      */
25865     stylesheets: false,
25866     
25867     // id of frame..
25868     frameId: false,
25869     
25870     // private properties
25871     validationEvent : false,
25872     deferHeight: true,
25873     initialized : false,
25874     activated : false,
25875     
25876     onFocus : Roo.emptyFn,
25877     iframePad:3,
25878     hideMode:'offsets',
25879     
25880     tbContainer : false,
25881     
25882     bodyCls : '',
25883     
25884     toolbarContainer :function() {
25885         return this.wrap.select('.x-html-editor-tb',true).first();
25886     },
25887
25888     /**
25889      * Protected method that will not generally be called directly. It
25890      * is called when the editor creates its toolbar. Override this method if you need to
25891      * add custom toolbar buttons.
25892      * @param {HtmlEditor} editor
25893      */
25894     createToolbar : function(){
25895         Roo.log('renewing');
25896         Roo.log("create toolbars");
25897         
25898         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25899         this.toolbars[0].render(this.toolbarContainer());
25900         
25901         return;
25902         
25903 //        if (!editor.toolbars || !editor.toolbars.length) {
25904 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25905 //        }
25906 //        
25907 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25908 //            editor.toolbars[i] = Roo.factory(
25909 //                    typeof(editor.toolbars[i]) == 'string' ?
25910 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25911 //                Roo.bootstrap.HtmlEditor);
25912 //            editor.toolbars[i].init(editor);
25913 //        }
25914     },
25915
25916      
25917     // private
25918     onRender : function(ct, position)
25919     {
25920        // Roo.log("Call onRender: " + this.xtype);
25921         var _t = this;
25922         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25923       
25924         this.wrap = this.inputEl().wrap({
25925             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25926         });
25927         
25928         this.editorcore.onRender(ct, position);
25929          
25930         if (this.resizable) {
25931             this.resizeEl = new Roo.Resizable(this.wrap, {
25932                 pinned : true,
25933                 wrap: true,
25934                 dynamic : true,
25935                 minHeight : this.height,
25936                 height: this.height,
25937                 handles : this.resizable,
25938                 width: this.width,
25939                 listeners : {
25940                     resize : function(r, w, h) {
25941                         _t.onResize(w,h); // -something
25942                     }
25943                 }
25944             });
25945             
25946         }
25947         this.createToolbar(this);
25948        
25949         
25950         if(!this.width && this.resizable){
25951             this.setSize(this.wrap.getSize());
25952         }
25953         if (this.resizeEl) {
25954             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25955             // should trigger onReize..
25956         }
25957         
25958     },
25959
25960     // private
25961     onResize : function(w, h)
25962     {
25963         Roo.log('resize: ' +w + ',' + h );
25964         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25965         var ew = false;
25966         var eh = false;
25967         
25968         if(this.inputEl() ){
25969             if(typeof w == 'number'){
25970                 var aw = w - this.wrap.getFrameWidth('lr');
25971                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25972                 ew = aw;
25973             }
25974             if(typeof h == 'number'){
25975                  var tbh = -11;  // fixme it needs to tool bar size!
25976                 for (var i =0; i < this.toolbars.length;i++) {
25977                     // fixme - ask toolbars for heights?
25978                     tbh += this.toolbars[i].el.getHeight();
25979                     //if (this.toolbars[i].footer) {
25980                     //    tbh += this.toolbars[i].footer.el.getHeight();
25981                     //}
25982                 }
25983               
25984                 
25985                 
25986                 
25987                 
25988                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25989                 ah -= 5; // knock a few pixes off for look..
25990                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25991                 var eh = ah;
25992             }
25993         }
25994         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25995         this.editorcore.onResize(ew,eh);
25996         
25997     },
25998
25999     /**
26000      * Toggles the editor between standard and source edit mode.
26001      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26002      */
26003     toggleSourceEdit : function(sourceEditMode)
26004     {
26005         this.editorcore.toggleSourceEdit(sourceEditMode);
26006         
26007         if(this.editorcore.sourceEditMode){
26008             Roo.log('editor - showing textarea');
26009             
26010 //            Roo.log('in');
26011 //            Roo.log(this.syncValue());
26012             this.syncValue();
26013             this.inputEl().removeClass(['hide', 'x-hidden']);
26014             this.inputEl().dom.removeAttribute('tabIndex');
26015             this.inputEl().focus();
26016         }else{
26017             Roo.log('editor - hiding textarea');
26018 //            Roo.log('out')
26019 //            Roo.log(this.pushValue()); 
26020             this.pushValue();
26021             
26022             this.inputEl().addClass(['hide', 'x-hidden']);
26023             this.inputEl().dom.setAttribute('tabIndex', -1);
26024             //this.deferFocus();
26025         }
26026          
26027         if(this.resizable){
26028             this.setSize(this.wrap.getSize());
26029         }
26030         
26031         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26032     },
26033  
26034     // private (for BoxComponent)
26035     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26036
26037     // private (for BoxComponent)
26038     getResizeEl : function(){
26039         return this.wrap;
26040     },
26041
26042     // private (for BoxComponent)
26043     getPositionEl : function(){
26044         return this.wrap;
26045     },
26046
26047     // private
26048     initEvents : function(){
26049         this.originalValue = this.getValue();
26050     },
26051
26052 //    /**
26053 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26054 //     * @method
26055 //     */
26056 //    markInvalid : Roo.emptyFn,
26057 //    /**
26058 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26059 //     * @method
26060 //     */
26061 //    clearInvalid : Roo.emptyFn,
26062
26063     setValue : function(v){
26064         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26065         this.editorcore.pushValue();
26066     },
26067
26068      
26069     // private
26070     deferFocus : function(){
26071         this.focus.defer(10, this);
26072     },
26073
26074     // doc'ed in Field
26075     focus : function(){
26076         this.editorcore.focus();
26077         
26078     },
26079       
26080
26081     // private
26082     onDestroy : function(){
26083         
26084         
26085         
26086         if(this.rendered){
26087             
26088             for (var i =0; i < this.toolbars.length;i++) {
26089                 // fixme - ask toolbars for heights?
26090                 this.toolbars[i].onDestroy();
26091             }
26092             
26093             this.wrap.dom.innerHTML = '';
26094             this.wrap.remove();
26095         }
26096     },
26097
26098     // private
26099     onFirstFocus : function(){
26100         //Roo.log("onFirstFocus");
26101         this.editorcore.onFirstFocus();
26102          for (var i =0; i < this.toolbars.length;i++) {
26103             this.toolbars[i].onFirstFocus();
26104         }
26105         
26106     },
26107     
26108     // private
26109     syncValue : function()
26110     {   
26111         this.editorcore.syncValue();
26112     },
26113     
26114     pushValue : function()
26115     {   
26116         this.editorcore.pushValue();
26117     }
26118      
26119     
26120     // hide stuff that is not compatible
26121     /**
26122      * @event blur
26123      * @hide
26124      */
26125     /**
26126      * @event change
26127      * @hide
26128      */
26129     /**
26130      * @event focus
26131      * @hide
26132      */
26133     /**
26134      * @event specialkey
26135      * @hide
26136      */
26137     /**
26138      * @cfg {String} fieldClass @hide
26139      */
26140     /**
26141      * @cfg {String} focusClass @hide
26142      */
26143     /**
26144      * @cfg {String} autoCreate @hide
26145      */
26146     /**
26147      * @cfg {String} inputType @hide
26148      */
26149      
26150     /**
26151      * @cfg {String} invalidText @hide
26152      */
26153     /**
26154      * @cfg {String} msgFx @hide
26155      */
26156     /**
26157      * @cfg {String} validateOnBlur @hide
26158      */
26159 });
26160  
26161     
26162    
26163    
26164    
26165       
26166 Roo.namespace('Roo.bootstrap.htmleditor');
26167 /**
26168  * @class Roo.bootstrap.HtmlEditorToolbar1
26169  * Basic Toolbar
26170  * 
26171  * @example
26172  * Usage:
26173  *
26174  new Roo.bootstrap.HtmlEditor({
26175     ....
26176     toolbars : [
26177         new Roo.bootstrap.HtmlEditorToolbar1({
26178             disable : { fonts: 1 , format: 1, ..., ... , ...],
26179             btns : [ .... ]
26180         })
26181     }
26182      
26183  * 
26184  * @cfg {Object} disable List of elements to disable..
26185  * @cfg {Array} btns List of additional buttons.
26186  * 
26187  * 
26188  * NEEDS Extra CSS? 
26189  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26190  */
26191  
26192 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26193 {
26194     
26195     Roo.apply(this, config);
26196     
26197     // default disabled, based on 'good practice'..
26198     this.disable = this.disable || {};
26199     Roo.applyIf(this.disable, {
26200         fontSize : true,
26201         colors : true,
26202         specialElements : true
26203     });
26204     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26205     
26206     this.editor = config.editor;
26207     this.editorcore = config.editor.editorcore;
26208     
26209     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26210     
26211     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26212     // dont call parent... till later.
26213 }
26214 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26215      
26216     bar : true,
26217     
26218     editor : false,
26219     editorcore : false,
26220     
26221     
26222     formats : [
26223         "p" ,  
26224         "h1","h2","h3","h4","h5","h6", 
26225         "pre", "code", 
26226         "abbr", "acronym", "address", "cite", "samp", "var",
26227         'div','span'
26228     ],
26229     
26230     onRender : function(ct, position)
26231     {
26232        // Roo.log("Call onRender: " + this.xtype);
26233         
26234        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26235        Roo.log(this.el);
26236        this.el.dom.style.marginBottom = '0';
26237        var _this = this;
26238        var editorcore = this.editorcore;
26239        var editor= this.editor;
26240        
26241        var children = [];
26242        var btn = function(id,cmd , toggle, handler, html){
26243        
26244             var  event = toggle ? 'toggle' : 'click';
26245        
26246             var a = {
26247                 size : 'sm',
26248                 xtype: 'Button',
26249                 xns: Roo.bootstrap,
26250                 //glyphicon : id,
26251                 fa: id,
26252                 cmd : id || cmd,
26253                 enableToggle:toggle !== false,
26254                 html : html || '',
26255                 pressed : toggle ? false : null,
26256                 listeners : {}
26257             };
26258             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26259                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26260             };
26261             children.push(a);
26262             return a;
26263        }
26264        
26265     //    var cb_box = function...
26266         
26267         var style = {
26268                 xtype: 'Button',
26269                 size : 'sm',
26270                 xns: Roo.bootstrap,
26271                 fa : 'font',
26272                 //html : 'submit'
26273                 menu : {
26274                     xtype: 'Menu',
26275                     xns: Roo.bootstrap,
26276                     items:  []
26277                 }
26278         };
26279         Roo.each(this.formats, function(f) {
26280             style.menu.items.push({
26281                 xtype :'MenuItem',
26282                 xns: Roo.bootstrap,
26283                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26284                 tagname : f,
26285                 listeners : {
26286                     click : function()
26287                     {
26288                         editorcore.insertTag(this.tagname);
26289                         editor.focus();
26290                     }
26291                 }
26292                 
26293             });
26294         });
26295         children.push(style);   
26296         
26297         btn('bold',false,true);
26298         btn('italic',false,true);
26299         btn('align-left', 'justifyleft',true);
26300         btn('align-center', 'justifycenter',true);
26301         btn('align-right' , 'justifyright',true);
26302         btn('link', false, false, function(btn) {
26303             //Roo.log("create link?");
26304             var url = prompt(this.createLinkText, this.defaultLinkValue);
26305             if(url && url != 'http:/'+'/'){
26306                 this.editorcore.relayCmd('createlink', url);
26307             }
26308         }),
26309         btn('list','insertunorderedlist',true);
26310         btn('pencil', false,true, function(btn){
26311                 Roo.log(this);
26312                 this.toggleSourceEdit(btn.pressed);
26313         });
26314         
26315         if (this.editor.btns.length > 0) {
26316             for (var i = 0; i<this.editor.btns.length; i++) {
26317                 children.push(this.editor.btns[i]);
26318             }
26319         }
26320         
26321         /*
26322         var cog = {
26323                 xtype: 'Button',
26324                 size : 'sm',
26325                 xns: Roo.bootstrap,
26326                 glyphicon : 'cog',
26327                 //html : 'submit'
26328                 menu : {
26329                     xtype: 'Menu',
26330                     xns: Roo.bootstrap,
26331                     items:  []
26332                 }
26333         };
26334         
26335         cog.menu.items.push({
26336             xtype :'MenuItem',
26337             xns: Roo.bootstrap,
26338             html : Clean styles,
26339             tagname : f,
26340             listeners : {
26341                 click : function()
26342                 {
26343                     editorcore.insertTag(this.tagname);
26344                     editor.focus();
26345                 }
26346             }
26347             
26348         });
26349        */
26350         
26351          
26352        this.xtype = 'NavSimplebar';
26353         
26354         for(var i=0;i< children.length;i++) {
26355             
26356             this.buttons.add(this.addxtypeChild(children[i]));
26357             
26358         }
26359         
26360         editor.on('editorevent', this.updateToolbar, this);
26361     },
26362     onBtnClick : function(id)
26363     {
26364        this.editorcore.relayCmd(id);
26365        this.editorcore.focus();
26366     },
26367     
26368     /**
26369      * Protected method that will not generally be called directly. It triggers
26370      * a toolbar update by reading the markup state of the current selection in the editor.
26371      */
26372     updateToolbar: function(){
26373
26374         if(!this.editorcore.activated){
26375             this.editor.onFirstFocus(); // is this neeed?
26376             return;
26377         }
26378
26379         var btns = this.buttons; 
26380         var doc = this.editorcore.doc;
26381         btns.get('bold').setActive(doc.queryCommandState('bold'));
26382         btns.get('italic').setActive(doc.queryCommandState('italic'));
26383         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26384         
26385         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26386         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26387         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26388         
26389         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26390         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26391          /*
26392         
26393         var ans = this.editorcore.getAllAncestors();
26394         if (this.formatCombo) {
26395             
26396             
26397             var store = this.formatCombo.store;
26398             this.formatCombo.setValue("");
26399             for (var i =0; i < ans.length;i++) {
26400                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26401                     // select it..
26402                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26403                     break;
26404                 }
26405             }
26406         }
26407         
26408         
26409         
26410         // hides menus... - so this cant be on a menu...
26411         Roo.bootstrap.MenuMgr.hideAll();
26412         */
26413         Roo.bootstrap.MenuMgr.hideAll();
26414         //this.editorsyncValue();
26415     },
26416     onFirstFocus: function() {
26417         this.buttons.each(function(item){
26418            item.enable();
26419         });
26420     },
26421     toggleSourceEdit : function(sourceEditMode){
26422         
26423           
26424         if(sourceEditMode){
26425             Roo.log("disabling buttons");
26426            this.buttons.each( function(item){
26427                 if(item.cmd != 'pencil'){
26428                     item.disable();
26429                 }
26430             });
26431           
26432         }else{
26433             Roo.log("enabling buttons");
26434             if(this.editorcore.initialized){
26435                 this.buttons.each( function(item){
26436                     item.enable();
26437                 });
26438             }
26439             
26440         }
26441         Roo.log("calling toggole on editor");
26442         // tell the editor that it's been pressed..
26443         this.editor.toggleSourceEdit(sourceEditMode);
26444        
26445     }
26446 });
26447
26448
26449
26450
26451  
26452 /*
26453  * - LGPL
26454  */
26455
26456 /**
26457  * @class Roo.bootstrap.Markdown
26458  * @extends Roo.bootstrap.TextArea
26459  * Bootstrap Showdown editable area
26460  * @cfg {string} content
26461  * 
26462  * @constructor
26463  * Create a new Showdown
26464  */
26465
26466 Roo.bootstrap.Markdown = function(config){
26467     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26468    
26469 };
26470
26471 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26472     
26473     editing :false,
26474     
26475     initEvents : function()
26476     {
26477         
26478         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26479         this.markdownEl = this.el.createChild({
26480             cls : 'roo-markdown-area'
26481         });
26482         this.inputEl().addClass('d-none');
26483         if (this.getValue() == '') {
26484             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26485             
26486         } else {
26487             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26488         }
26489         this.markdownEl.on('click', this.toggleTextEdit, this);
26490         this.on('blur', this.toggleTextEdit, this);
26491         this.on('specialkey', this.resizeTextArea, this);
26492     },
26493     
26494     toggleTextEdit : function()
26495     {
26496         var sh = this.markdownEl.getHeight();
26497         this.inputEl().addClass('d-none');
26498         this.markdownEl.addClass('d-none');
26499         if (!this.editing) {
26500             // show editor?
26501             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26502             this.inputEl().removeClass('d-none');
26503             this.inputEl().focus();
26504             this.editing = true;
26505             return;
26506         }
26507         // show showdown...
26508         this.updateMarkdown();
26509         this.markdownEl.removeClass('d-none');
26510         this.editing = false;
26511         return;
26512     },
26513     updateMarkdown : function()
26514     {
26515         if (this.getValue() == '') {
26516             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26517             return;
26518         }
26519  
26520         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26521     },
26522     
26523     resizeTextArea: function () {
26524         
26525         var sh = 100;
26526         Roo.log([sh, this.getValue().split("\n").length * 30]);
26527         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26528     },
26529     setValue : function(val)
26530     {
26531         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26532         if (!this.editing) {
26533             this.updateMarkdown();
26534         }
26535         
26536     },
26537     focus : function()
26538     {
26539         if (!this.editing) {
26540             this.toggleTextEdit();
26541         }
26542         
26543     }
26544
26545
26546 });
26547 /**
26548  * @class Roo.bootstrap.Table.AbstractSelectionModel
26549  * @extends Roo.util.Observable
26550  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26551  * implemented by descendant classes.  This class should not be directly instantiated.
26552  * @constructor
26553  */
26554 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26555     this.locked = false;
26556     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26557 };
26558
26559
26560 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26561     /** @ignore Called by the grid automatically. Do not call directly. */
26562     init : function(grid){
26563         this.grid = grid;
26564         this.initEvents();
26565     },
26566
26567     /**
26568      * Locks the selections.
26569      */
26570     lock : function(){
26571         this.locked = true;
26572     },
26573
26574     /**
26575      * Unlocks the selections.
26576      */
26577     unlock : function(){
26578         this.locked = false;
26579     },
26580
26581     /**
26582      * Returns true if the selections are locked.
26583      * @return {Boolean}
26584      */
26585     isLocked : function(){
26586         return this.locked;
26587     },
26588     
26589     
26590     initEvents : function ()
26591     {
26592         
26593     }
26594 });
26595 /**
26596  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26597  * @class Roo.bootstrap.Table.RowSelectionModel
26598  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26599  * It supports multiple selections and keyboard selection/navigation. 
26600  * @constructor
26601  * @param {Object} config
26602  */
26603
26604 Roo.bootstrap.Table.RowSelectionModel = function(config){
26605     Roo.apply(this, config);
26606     this.selections = new Roo.util.MixedCollection(false, function(o){
26607         return o.id;
26608     });
26609
26610     this.last = false;
26611     this.lastActive = false;
26612
26613     this.addEvents({
26614         /**
26615              * @event selectionchange
26616              * Fires when the selection changes
26617              * @param {SelectionModel} this
26618              */
26619             "selectionchange" : true,
26620         /**
26621              * @event afterselectionchange
26622              * Fires after the selection changes (eg. by key press or clicking)
26623              * @param {SelectionModel} this
26624              */
26625             "afterselectionchange" : true,
26626         /**
26627              * @event beforerowselect
26628              * Fires when a row is selected being selected, return false to cancel.
26629              * @param {SelectionModel} this
26630              * @param {Number} rowIndex The selected index
26631              * @param {Boolean} keepExisting False if other selections will be cleared
26632              */
26633             "beforerowselect" : true,
26634         /**
26635              * @event rowselect
26636              * Fires when a row is selected.
26637              * @param {SelectionModel} this
26638              * @param {Number} rowIndex The selected index
26639              * @param {Roo.data.Record} r The record
26640              */
26641             "rowselect" : true,
26642         /**
26643              * @event rowdeselect
26644              * Fires when a row is deselected.
26645              * @param {SelectionModel} this
26646              * @param {Number} rowIndex The selected index
26647              */
26648         "rowdeselect" : true
26649     });
26650     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26651     this.locked = false;
26652  };
26653
26654 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26655     /**
26656      * @cfg {Boolean} singleSelect
26657      * True to allow selection of only one row at a time (defaults to false)
26658      */
26659     singleSelect : false,
26660
26661     // private
26662     initEvents : function()
26663     {
26664
26665         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26666         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26667         //}else{ // allow click to work like normal
26668          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26669         //}
26670         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26671         this.grid.on("rowclick", this.handleMouseDown, this);
26672         
26673         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26674             "up" : function(e){
26675                 if(!e.shiftKey){
26676                     this.selectPrevious(e.shiftKey);
26677                 }else if(this.last !== false && this.lastActive !== false){
26678                     var last = this.last;
26679                     this.selectRange(this.last,  this.lastActive-1);
26680                     this.grid.getView().focusRow(this.lastActive);
26681                     if(last !== false){
26682                         this.last = last;
26683                     }
26684                 }else{
26685                     this.selectFirstRow();
26686                 }
26687                 this.fireEvent("afterselectionchange", this);
26688             },
26689             "down" : function(e){
26690                 if(!e.shiftKey){
26691                     this.selectNext(e.shiftKey);
26692                 }else if(this.last !== false && this.lastActive !== false){
26693                     var last = this.last;
26694                     this.selectRange(this.last,  this.lastActive+1);
26695                     this.grid.getView().focusRow(this.lastActive);
26696                     if(last !== false){
26697                         this.last = last;
26698                     }
26699                 }else{
26700                     this.selectFirstRow();
26701                 }
26702                 this.fireEvent("afterselectionchange", this);
26703             },
26704             scope: this
26705         });
26706         this.grid.store.on('load', function(){
26707             this.selections.clear();
26708         },this);
26709         /*
26710         var view = this.grid.view;
26711         view.on("refresh", this.onRefresh, this);
26712         view.on("rowupdated", this.onRowUpdated, this);
26713         view.on("rowremoved", this.onRemove, this);
26714         */
26715     },
26716
26717     // private
26718     onRefresh : function()
26719     {
26720         var ds = this.grid.store, i, v = this.grid.view;
26721         var s = this.selections;
26722         s.each(function(r){
26723             if((i = ds.indexOfId(r.id)) != -1){
26724                 v.onRowSelect(i);
26725             }else{
26726                 s.remove(r);
26727             }
26728         });
26729     },
26730
26731     // private
26732     onRemove : function(v, index, r){
26733         this.selections.remove(r);
26734     },
26735
26736     // private
26737     onRowUpdated : function(v, index, r){
26738         if(this.isSelected(r)){
26739             v.onRowSelect(index);
26740         }
26741     },
26742
26743     /**
26744      * Select records.
26745      * @param {Array} records The records to select
26746      * @param {Boolean} keepExisting (optional) True to keep existing selections
26747      */
26748     selectRecords : function(records, keepExisting)
26749     {
26750         if(!keepExisting){
26751             this.clearSelections();
26752         }
26753             var ds = this.grid.store;
26754         for(var i = 0, len = records.length; i < len; i++){
26755             this.selectRow(ds.indexOf(records[i]), true);
26756         }
26757     },
26758
26759     /**
26760      * Gets the number of selected rows.
26761      * @return {Number}
26762      */
26763     getCount : function(){
26764         return this.selections.length;
26765     },
26766
26767     /**
26768      * Selects the first row in the grid.
26769      */
26770     selectFirstRow : function(){
26771         this.selectRow(0);
26772     },
26773
26774     /**
26775      * Select the last row.
26776      * @param {Boolean} keepExisting (optional) True to keep existing selections
26777      */
26778     selectLastRow : function(keepExisting){
26779         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26780         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26781     },
26782
26783     /**
26784      * Selects the row immediately following the last selected row.
26785      * @param {Boolean} keepExisting (optional) True to keep existing selections
26786      */
26787     selectNext : function(keepExisting)
26788     {
26789             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26790             this.selectRow(this.last+1, keepExisting);
26791             this.grid.getView().focusRow(this.last);
26792         }
26793     },
26794
26795     /**
26796      * Selects the row that precedes the last selected row.
26797      * @param {Boolean} keepExisting (optional) True to keep existing selections
26798      */
26799     selectPrevious : function(keepExisting){
26800         if(this.last){
26801             this.selectRow(this.last-1, keepExisting);
26802             this.grid.getView().focusRow(this.last);
26803         }
26804     },
26805
26806     /**
26807      * Returns the selected records
26808      * @return {Array} Array of selected records
26809      */
26810     getSelections : function(){
26811         return [].concat(this.selections.items);
26812     },
26813
26814     /**
26815      * Returns the first selected record.
26816      * @return {Record}
26817      */
26818     getSelected : function(){
26819         return this.selections.itemAt(0);
26820     },
26821
26822
26823     /**
26824      * Clears all selections.
26825      */
26826     clearSelections : function(fast)
26827     {
26828         if(this.locked) {
26829             return;
26830         }
26831         if(fast !== true){
26832                 var ds = this.grid.store;
26833             var s = this.selections;
26834             s.each(function(r){
26835                 this.deselectRow(ds.indexOfId(r.id));
26836             }, this);
26837             s.clear();
26838         }else{
26839             this.selections.clear();
26840         }
26841         this.last = false;
26842     },
26843
26844
26845     /**
26846      * Selects all rows.
26847      */
26848     selectAll : function(){
26849         if(this.locked) {
26850             return;
26851         }
26852         this.selections.clear();
26853         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26854             this.selectRow(i, true);
26855         }
26856     },
26857
26858     /**
26859      * Returns True if there is a selection.
26860      * @return {Boolean}
26861      */
26862     hasSelection : function(){
26863         return this.selections.length > 0;
26864     },
26865
26866     /**
26867      * Returns True if the specified row is selected.
26868      * @param {Number/Record} record The record or index of the record to check
26869      * @return {Boolean}
26870      */
26871     isSelected : function(index){
26872             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26873         return (r && this.selections.key(r.id) ? true : false);
26874     },
26875
26876     /**
26877      * Returns True if the specified record id is selected.
26878      * @param {String} id The id of record to check
26879      * @return {Boolean}
26880      */
26881     isIdSelected : function(id){
26882         return (this.selections.key(id) ? true : false);
26883     },
26884
26885
26886     // private
26887     handleMouseDBClick : function(e, t){
26888         
26889     },
26890     // private
26891     handleMouseDown : function(e, t)
26892     {
26893             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26894         if(this.isLocked() || rowIndex < 0 ){
26895             return;
26896         };
26897         if(e.shiftKey && this.last !== false){
26898             var last = this.last;
26899             this.selectRange(last, rowIndex, e.ctrlKey);
26900             this.last = last; // reset the last
26901             t.focus();
26902     
26903         }else{
26904             var isSelected = this.isSelected(rowIndex);
26905             //Roo.log("select row:" + rowIndex);
26906             if(isSelected){
26907                 this.deselectRow(rowIndex);
26908             } else {
26909                         this.selectRow(rowIndex, true);
26910             }
26911     
26912             /*
26913                 if(e.button !== 0 && isSelected){
26914                 alert('rowIndex 2: ' + rowIndex);
26915                     view.focusRow(rowIndex);
26916                 }else if(e.ctrlKey && isSelected){
26917                     this.deselectRow(rowIndex);
26918                 }else if(!isSelected){
26919                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26920                     view.focusRow(rowIndex);
26921                 }
26922             */
26923         }
26924         this.fireEvent("afterselectionchange", this);
26925     },
26926     // private
26927     handleDragableRowClick :  function(grid, rowIndex, e) 
26928     {
26929         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26930             this.selectRow(rowIndex, false);
26931             grid.view.focusRow(rowIndex);
26932              this.fireEvent("afterselectionchange", this);
26933         }
26934     },
26935     
26936     /**
26937      * Selects multiple rows.
26938      * @param {Array} rows Array of the indexes of the row to select
26939      * @param {Boolean} keepExisting (optional) True to keep existing selections
26940      */
26941     selectRows : function(rows, keepExisting){
26942         if(!keepExisting){
26943             this.clearSelections();
26944         }
26945         for(var i = 0, len = rows.length; i < len; i++){
26946             this.selectRow(rows[i], true);
26947         }
26948     },
26949
26950     /**
26951      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26952      * @param {Number} startRow The index of the first row in the range
26953      * @param {Number} endRow The index of the last row in the range
26954      * @param {Boolean} keepExisting (optional) True to retain existing selections
26955      */
26956     selectRange : function(startRow, endRow, keepExisting){
26957         if(this.locked) {
26958             return;
26959         }
26960         if(!keepExisting){
26961             this.clearSelections();
26962         }
26963         if(startRow <= endRow){
26964             for(var i = startRow; i <= endRow; i++){
26965                 this.selectRow(i, true);
26966             }
26967         }else{
26968             for(var i = startRow; i >= endRow; i--){
26969                 this.selectRow(i, true);
26970             }
26971         }
26972     },
26973
26974     /**
26975      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26976      * @param {Number} startRow The index of the first row in the range
26977      * @param {Number} endRow The index of the last row in the range
26978      */
26979     deselectRange : function(startRow, endRow, preventViewNotify){
26980         if(this.locked) {
26981             return;
26982         }
26983         for(var i = startRow; i <= endRow; i++){
26984             this.deselectRow(i, preventViewNotify);
26985         }
26986     },
26987
26988     /**
26989      * Selects a row.
26990      * @param {Number} row The index of the row to select
26991      * @param {Boolean} keepExisting (optional) True to keep existing selections
26992      */
26993     selectRow : function(index, keepExisting, preventViewNotify)
26994     {
26995             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26996             return;
26997         }
26998         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26999             if(!keepExisting || this.singleSelect){
27000                 this.clearSelections();
27001             }
27002             
27003             var r = this.grid.store.getAt(index);
27004             //console.log('selectRow - record id :' + r.id);
27005             
27006             this.selections.add(r);
27007             this.last = this.lastActive = index;
27008             if(!preventViewNotify){
27009                 var proxy = new Roo.Element(
27010                                 this.grid.getRowDom(index)
27011                 );
27012                 proxy.addClass('bg-info info');
27013             }
27014             this.fireEvent("rowselect", this, index, r);
27015             this.fireEvent("selectionchange", this);
27016         }
27017     },
27018
27019     /**
27020      * Deselects a row.
27021      * @param {Number} row The index of the row to deselect
27022      */
27023     deselectRow : function(index, preventViewNotify)
27024     {
27025         if(this.locked) {
27026             return;
27027         }
27028         if(this.last == index){
27029             this.last = false;
27030         }
27031         if(this.lastActive == index){
27032             this.lastActive = false;
27033         }
27034         
27035         var r = this.grid.store.getAt(index);
27036         if (!r) {
27037             return;
27038         }
27039         
27040         this.selections.remove(r);
27041         //.console.log('deselectRow - record id :' + r.id);
27042         if(!preventViewNotify){
27043         
27044             var proxy = new Roo.Element(
27045                 this.grid.getRowDom(index)
27046             );
27047             proxy.removeClass('bg-info info');
27048         }
27049         this.fireEvent("rowdeselect", this, index);
27050         this.fireEvent("selectionchange", this);
27051     },
27052
27053     // private
27054     restoreLast : function(){
27055         if(this._last){
27056             this.last = this._last;
27057         }
27058     },
27059
27060     // private
27061     acceptsNav : function(row, col, cm){
27062         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27063     },
27064
27065     // private
27066     onEditorKey : function(field, e){
27067         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27068         if(k == e.TAB){
27069             e.stopEvent();
27070             ed.completeEdit();
27071             if(e.shiftKey){
27072                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27073             }else{
27074                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27075             }
27076         }else if(k == e.ENTER && !e.ctrlKey){
27077             e.stopEvent();
27078             ed.completeEdit();
27079             if(e.shiftKey){
27080                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27081             }else{
27082                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27083             }
27084         }else if(k == e.ESC){
27085             ed.cancelEdit();
27086         }
27087         if(newCell){
27088             g.startEditing(newCell[0], newCell[1]);
27089         }
27090     }
27091 });
27092 /*
27093  * Based on:
27094  * Ext JS Library 1.1.1
27095  * Copyright(c) 2006-2007, Ext JS, LLC.
27096  *
27097  * Originally Released Under LGPL - original licence link has changed is not relivant.
27098  *
27099  * Fork - LGPL
27100  * <script type="text/javascript">
27101  */
27102  
27103 /**
27104  * @class Roo.bootstrap.PagingToolbar
27105  * @extends Roo.bootstrap.NavSimplebar
27106  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27107  * @constructor
27108  * Create a new PagingToolbar
27109  * @param {Object} config The config object
27110  * @param {Roo.data.Store} store
27111  */
27112 Roo.bootstrap.PagingToolbar = function(config)
27113 {
27114     // old args format still supported... - xtype is prefered..
27115         // created from xtype...
27116     
27117     this.ds = config.dataSource;
27118     
27119     if (config.store && !this.ds) {
27120         this.store= Roo.factory(config.store, Roo.data);
27121         this.ds = this.store;
27122         this.ds.xmodule = this.xmodule || false;
27123     }
27124     
27125     this.toolbarItems = [];
27126     if (config.items) {
27127         this.toolbarItems = config.items;
27128     }
27129     
27130     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27131     
27132     this.cursor = 0;
27133     
27134     if (this.ds) { 
27135         this.bind(this.ds);
27136     }
27137     
27138     if (Roo.bootstrap.version == 4) {
27139         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27140     } else {
27141         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27142     }
27143     
27144 };
27145
27146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27147     /**
27148      * @cfg {Roo.data.Store} dataSource
27149      * The underlying data store providing the paged data
27150      */
27151     /**
27152      * @cfg {String/HTMLElement/Element} container
27153      * container The id or element that will contain the toolbar
27154      */
27155     /**
27156      * @cfg {Boolean} displayInfo
27157      * True to display the displayMsg (defaults to false)
27158      */
27159     /**
27160      * @cfg {Number} pageSize
27161      * The number of records to display per page (defaults to 20)
27162      */
27163     pageSize: 20,
27164     /**
27165      * @cfg {String} displayMsg
27166      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27167      */
27168     displayMsg : 'Displaying {0} - {1} of {2}',
27169     /**
27170      * @cfg {String} emptyMsg
27171      * The message to display when no records are found (defaults to "No data to display")
27172      */
27173     emptyMsg : 'No data to display',
27174     /**
27175      * Customizable piece of the default paging text (defaults to "Page")
27176      * @type String
27177      */
27178     beforePageText : "Page",
27179     /**
27180      * Customizable piece of the default paging text (defaults to "of %0")
27181      * @type String
27182      */
27183     afterPageText : "of {0}",
27184     /**
27185      * Customizable piece of the default paging text (defaults to "First Page")
27186      * @type String
27187      */
27188     firstText : "First Page",
27189     /**
27190      * Customizable piece of the default paging text (defaults to "Previous Page")
27191      * @type String
27192      */
27193     prevText : "Previous Page",
27194     /**
27195      * Customizable piece of the default paging text (defaults to "Next Page")
27196      * @type String
27197      */
27198     nextText : "Next Page",
27199     /**
27200      * Customizable piece of the default paging text (defaults to "Last Page")
27201      * @type String
27202      */
27203     lastText : "Last Page",
27204     /**
27205      * Customizable piece of the default paging text (defaults to "Refresh")
27206      * @type String
27207      */
27208     refreshText : "Refresh",
27209
27210     buttons : false,
27211     // private
27212     onRender : function(ct, position) 
27213     {
27214         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27215         this.navgroup.parentId = this.id;
27216         this.navgroup.onRender(this.el, null);
27217         // add the buttons to the navgroup
27218         
27219         if(this.displayInfo){
27220             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27221             this.displayEl = this.el.select('.x-paging-info', true).first();
27222 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27223 //            this.displayEl = navel.el.select('span',true).first();
27224         }
27225         
27226         var _this = this;
27227         
27228         if(this.buttons){
27229             Roo.each(_this.buttons, function(e){ // this might need to use render????
27230                Roo.factory(e).render(_this.el);
27231             });
27232         }
27233             
27234         Roo.each(_this.toolbarItems, function(e) {
27235             _this.navgroup.addItem(e);
27236         });
27237         
27238         
27239         this.first = this.navgroup.addItem({
27240             tooltip: this.firstText,
27241             cls: "prev btn-outline-secondary",
27242             html : ' <i class="fa fa-step-backward"></i>',
27243             disabled: true,
27244             preventDefault: true,
27245             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27246         });
27247         
27248         this.prev =  this.navgroup.addItem({
27249             tooltip: this.prevText,
27250             cls: "prev btn-outline-secondary",
27251             html : ' <i class="fa fa-backward"></i>',
27252             disabled: true,
27253             preventDefault: true,
27254             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27255         });
27256     //this.addSeparator();
27257         
27258         
27259         var field = this.navgroup.addItem( {
27260             tagtype : 'span',
27261             cls : 'x-paging-position  btn-outline-secondary',
27262              disabled: true,
27263             html : this.beforePageText  +
27264                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27265                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27266          } ); //?? escaped?
27267         
27268         this.field = field.el.select('input', true).first();
27269         this.field.on("keydown", this.onPagingKeydown, this);
27270         this.field.on("focus", function(){this.dom.select();});
27271     
27272     
27273         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27274         //this.field.setHeight(18);
27275         //this.addSeparator();
27276         this.next = this.navgroup.addItem({
27277             tooltip: this.nextText,
27278             cls: "next btn-outline-secondary",
27279             html : ' <i class="fa fa-forward"></i>',
27280             disabled: true,
27281             preventDefault: true,
27282             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27283         });
27284         this.last = this.navgroup.addItem({
27285             tooltip: this.lastText,
27286             html : ' <i class="fa fa-step-forward"></i>',
27287             cls: "next btn-outline-secondary",
27288             disabled: true,
27289             preventDefault: true,
27290             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27291         });
27292     //this.addSeparator();
27293         this.loading = this.navgroup.addItem({
27294             tooltip: this.refreshText,
27295             cls: "btn-outline-secondary",
27296             html : ' <i class="fa fa-refresh"></i>',
27297             preventDefault: true,
27298             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27299         });
27300         
27301     },
27302
27303     // private
27304     updateInfo : function(){
27305         if(this.displayEl){
27306             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27307             var msg = count == 0 ?
27308                 this.emptyMsg :
27309                 String.format(
27310                     this.displayMsg,
27311                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27312                 );
27313             this.displayEl.update(msg);
27314         }
27315     },
27316
27317     // private
27318     onLoad : function(ds, r, o)
27319     {
27320         this.cursor = o.params && o.params.start ? o.params.start : 0;
27321         
27322         var d = this.getPageData(),
27323             ap = d.activePage,
27324             ps = d.pages;
27325         
27326         
27327         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27328         this.field.dom.value = ap;
27329         this.first.setDisabled(ap == 1);
27330         this.prev.setDisabled(ap == 1);
27331         this.next.setDisabled(ap == ps);
27332         this.last.setDisabled(ap == ps);
27333         this.loading.enable();
27334         this.updateInfo();
27335     },
27336
27337     // private
27338     getPageData : function(){
27339         var total = this.ds.getTotalCount();
27340         return {
27341             total : total,
27342             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27343             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27344         };
27345     },
27346
27347     // private
27348     onLoadError : function(){
27349         this.loading.enable();
27350     },
27351
27352     // private
27353     onPagingKeydown : function(e){
27354         var k = e.getKey();
27355         var d = this.getPageData();
27356         if(k == e.RETURN){
27357             var v = this.field.dom.value, pageNum;
27358             if(!v || isNaN(pageNum = parseInt(v, 10))){
27359                 this.field.dom.value = d.activePage;
27360                 return;
27361             }
27362             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27363             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27364             e.stopEvent();
27365         }
27366         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))
27367         {
27368           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27369           this.field.dom.value = pageNum;
27370           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27371           e.stopEvent();
27372         }
27373         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27374         {
27375           var v = this.field.dom.value, pageNum; 
27376           var increment = (e.shiftKey) ? 10 : 1;
27377           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27378                 increment *= -1;
27379           }
27380           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27381             this.field.dom.value = d.activePage;
27382             return;
27383           }
27384           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27385           {
27386             this.field.dom.value = parseInt(v, 10) + increment;
27387             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27388             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27389           }
27390           e.stopEvent();
27391         }
27392     },
27393
27394     // private
27395     beforeLoad : function(){
27396         if(this.loading){
27397             this.loading.disable();
27398         }
27399     },
27400
27401     // private
27402     onClick : function(which){
27403         
27404         var ds = this.ds;
27405         if (!ds) {
27406             return;
27407         }
27408         
27409         switch(which){
27410             case "first":
27411                 ds.load({params:{start: 0, limit: this.pageSize}});
27412             break;
27413             case "prev":
27414                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27415             break;
27416             case "next":
27417                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27418             break;
27419             case "last":
27420                 var total = ds.getTotalCount();
27421                 var extra = total % this.pageSize;
27422                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27423                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27424             break;
27425             case "refresh":
27426                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27427             break;
27428         }
27429     },
27430
27431     /**
27432      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27433      * @param {Roo.data.Store} store The data store to unbind
27434      */
27435     unbind : function(ds){
27436         ds.un("beforeload", this.beforeLoad, this);
27437         ds.un("load", this.onLoad, this);
27438         ds.un("loadexception", this.onLoadError, this);
27439         ds.un("remove", this.updateInfo, this);
27440         ds.un("add", this.updateInfo, this);
27441         this.ds = undefined;
27442     },
27443
27444     /**
27445      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27446      * @param {Roo.data.Store} store The data store to bind
27447      */
27448     bind : function(ds){
27449         ds.on("beforeload", this.beforeLoad, this);
27450         ds.on("load", this.onLoad, this);
27451         ds.on("loadexception", this.onLoadError, this);
27452         ds.on("remove", this.updateInfo, this);
27453         ds.on("add", this.updateInfo, this);
27454         this.ds = ds;
27455     }
27456 });/*
27457  * - LGPL
27458  *
27459  * element
27460  * 
27461  */
27462
27463 /**
27464  * @class Roo.bootstrap.MessageBar
27465  * @extends Roo.bootstrap.Component
27466  * Bootstrap MessageBar class
27467  * @cfg {String} html contents of the MessageBar
27468  * @cfg {String} weight (info | success | warning | danger) default info
27469  * @cfg {String} beforeClass insert the bar before the given class
27470  * @cfg {Boolean} closable (true | false) default false
27471  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27472  * 
27473  * @constructor
27474  * Create a new Element
27475  * @param {Object} config The config object
27476  */
27477
27478 Roo.bootstrap.MessageBar = function(config){
27479     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27480 };
27481
27482 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27483     
27484     html: '',
27485     weight: 'info',
27486     closable: false,
27487     fixed: false,
27488     beforeClass: 'bootstrap-sticky-wrap',
27489     
27490     getAutoCreate : function(){
27491         
27492         var cfg = {
27493             tag: 'div',
27494             cls: 'alert alert-dismissable alert-' + this.weight,
27495             cn: [
27496                 {
27497                     tag: 'span',
27498                     cls: 'message',
27499                     html: this.html || ''
27500                 }
27501             ]
27502         };
27503         
27504         if(this.fixed){
27505             cfg.cls += ' alert-messages-fixed';
27506         }
27507         
27508         if(this.closable){
27509             cfg.cn.push({
27510                 tag: 'button',
27511                 cls: 'close',
27512                 html: 'x'
27513             });
27514         }
27515         
27516         return cfg;
27517     },
27518     
27519     onRender : function(ct, position)
27520     {
27521         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27522         
27523         if(!this.el){
27524             var cfg = Roo.apply({},  this.getAutoCreate());
27525             cfg.id = Roo.id();
27526             
27527             if (this.cls) {
27528                 cfg.cls += ' ' + this.cls;
27529             }
27530             if (this.style) {
27531                 cfg.style = this.style;
27532             }
27533             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27534             
27535             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27536         }
27537         
27538         this.el.select('>button.close').on('click', this.hide, this);
27539         
27540     },
27541     
27542     show : function()
27543     {
27544         if (!this.rendered) {
27545             this.render();
27546         }
27547         
27548         this.el.show();
27549         
27550         this.fireEvent('show', this);
27551         
27552     },
27553     
27554     hide : function()
27555     {
27556         if (!this.rendered) {
27557             this.render();
27558         }
27559         
27560         this.el.hide();
27561         
27562         this.fireEvent('hide', this);
27563     },
27564     
27565     update : function()
27566     {
27567 //        var e = this.el.dom.firstChild;
27568 //        
27569 //        if(this.closable){
27570 //            e = e.nextSibling;
27571 //        }
27572 //        
27573 //        e.data = this.html || '';
27574
27575         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27576     }
27577    
27578 });
27579
27580  
27581
27582      /*
27583  * - LGPL
27584  *
27585  * Graph
27586  * 
27587  */
27588
27589
27590 /**
27591  * @class Roo.bootstrap.Graph
27592  * @extends Roo.bootstrap.Component
27593  * Bootstrap Graph class
27594 > Prameters
27595  -sm {number} sm 4
27596  -md {number} md 5
27597  @cfg {String} graphtype  bar | vbar | pie
27598  @cfg {number} g_x coodinator | centre x (pie)
27599  @cfg {number} g_y coodinator | centre y (pie)
27600  @cfg {number} g_r radius (pie)
27601  @cfg {number} g_height height of the chart (respected by all elements in the set)
27602  @cfg {number} g_width width of the chart (respected by all elements in the set)
27603  @cfg {Object} title The title of the chart
27604     
27605  -{Array}  values
27606  -opts (object) options for the chart 
27607      o {
27608      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27609      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27610      o vgutter (number)
27611      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.
27612      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27613      o to
27614      o stretch (boolean)
27615      o }
27616  -opts (object) options for the pie
27617      o{
27618      o cut
27619      o startAngle (number)
27620      o endAngle (number)
27621      } 
27622  *
27623  * @constructor
27624  * Create a new Input
27625  * @param {Object} config The config object
27626  */
27627
27628 Roo.bootstrap.Graph = function(config){
27629     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27630     
27631     this.addEvents({
27632         // img events
27633         /**
27634          * @event click
27635          * The img click event for the img.
27636          * @param {Roo.EventObject} e
27637          */
27638         "click" : true
27639     });
27640 };
27641
27642 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27643     
27644     sm: 4,
27645     md: 5,
27646     graphtype: 'bar',
27647     g_height: 250,
27648     g_width: 400,
27649     g_x: 50,
27650     g_y: 50,
27651     g_r: 30,
27652     opts:{
27653         //g_colors: this.colors,
27654         g_type: 'soft',
27655         g_gutter: '20%'
27656
27657     },
27658     title : false,
27659
27660     getAutoCreate : function(){
27661         
27662         var cfg = {
27663             tag: 'div',
27664             html : null
27665         };
27666         
27667         
27668         return  cfg;
27669     },
27670
27671     onRender : function(ct,position){
27672         
27673         
27674         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27675         
27676         if (typeof(Raphael) == 'undefined') {
27677             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27678             return;
27679         }
27680         
27681         this.raphael = Raphael(this.el.dom);
27682         
27683                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27684                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27685                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27686                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27687                 /*
27688                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27689                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27690                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27691                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27692                 
27693                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27694                 r.barchart(330, 10, 300, 220, data1);
27695                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27696                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27697                 */
27698                 
27699                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27700                 // r.barchart(30, 30, 560, 250,  xdata, {
27701                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27702                 //     axis : "0 0 1 1",
27703                 //     axisxlabels :  xdata
27704                 //     //yvalues : cols,
27705                    
27706                 // });
27707 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27708 //        
27709 //        this.load(null,xdata,{
27710 //                axis : "0 0 1 1",
27711 //                axisxlabels :  xdata
27712 //                });
27713
27714     },
27715
27716     load : function(graphtype,xdata,opts)
27717     {
27718         this.raphael.clear();
27719         if(!graphtype) {
27720             graphtype = this.graphtype;
27721         }
27722         if(!opts){
27723             opts = this.opts;
27724         }
27725         var r = this.raphael,
27726             fin = function () {
27727                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27728             },
27729             fout = function () {
27730                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27731             },
27732             pfin = function() {
27733                 this.sector.stop();
27734                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27735
27736                 if (this.label) {
27737                     this.label[0].stop();
27738                     this.label[0].attr({ r: 7.5 });
27739                     this.label[1].attr({ "font-weight": 800 });
27740                 }
27741             },
27742             pfout = function() {
27743                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27744
27745                 if (this.label) {
27746                     this.label[0].animate({ r: 5 }, 500, "bounce");
27747                     this.label[1].attr({ "font-weight": 400 });
27748                 }
27749             };
27750
27751         switch(graphtype){
27752             case 'bar':
27753                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27754                 break;
27755             case 'hbar':
27756                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27757                 break;
27758             case 'pie':
27759 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27760 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27761 //            
27762                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27763                 
27764                 break;
27765
27766         }
27767         
27768         if(this.title){
27769             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27770         }
27771         
27772     },
27773     
27774     setTitle: function(o)
27775     {
27776         this.title = o;
27777     },
27778     
27779     initEvents: function() {
27780         
27781         if(!this.href){
27782             this.el.on('click', this.onClick, this);
27783         }
27784     },
27785     
27786     onClick : function(e)
27787     {
27788         Roo.log('img onclick');
27789         this.fireEvent('click', this, e);
27790     }
27791    
27792 });
27793
27794  
27795 /*
27796  * - LGPL
27797  *
27798  * numberBox
27799  * 
27800  */
27801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27802
27803 /**
27804  * @class Roo.bootstrap.dash.NumberBox
27805  * @extends Roo.bootstrap.Component
27806  * Bootstrap NumberBox class
27807  * @cfg {String} headline Box headline
27808  * @cfg {String} content Box content
27809  * @cfg {String} icon Box icon
27810  * @cfg {String} footer Footer text
27811  * @cfg {String} fhref Footer href
27812  * 
27813  * @constructor
27814  * Create a new NumberBox
27815  * @param {Object} config The config object
27816  */
27817
27818
27819 Roo.bootstrap.dash.NumberBox = function(config){
27820     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27821     
27822 };
27823
27824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27825     
27826     headline : '',
27827     content : '',
27828     icon : '',
27829     footer : '',
27830     fhref : '',
27831     ficon : '',
27832     
27833     getAutoCreate : function(){
27834         
27835         var cfg = {
27836             tag : 'div',
27837             cls : 'small-box ',
27838             cn : [
27839                 {
27840                     tag : 'div',
27841                     cls : 'inner',
27842                     cn :[
27843                         {
27844                             tag : 'h3',
27845                             cls : 'roo-headline',
27846                             html : this.headline
27847                         },
27848                         {
27849                             tag : 'p',
27850                             cls : 'roo-content',
27851                             html : this.content
27852                         }
27853                     ]
27854                 }
27855             ]
27856         };
27857         
27858         if(this.icon){
27859             cfg.cn.push({
27860                 tag : 'div',
27861                 cls : 'icon',
27862                 cn :[
27863                     {
27864                         tag : 'i',
27865                         cls : 'ion ' + this.icon
27866                     }
27867                 ]
27868             });
27869         }
27870         
27871         if(this.footer){
27872             var footer = {
27873                 tag : 'a',
27874                 cls : 'small-box-footer',
27875                 href : this.fhref || '#',
27876                 html : this.footer
27877             };
27878             
27879             cfg.cn.push(footer);
27880             
27881         }
27882         
27883         return  cfg;
27884     },
27885
27886     onRender : function(ct,position){
27887         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27888
27889
27890        
27891                 
27892     },
27893
27894     setHeadline: function (value)
27895     {
27896         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27897     },
27898     
27899     setFooter: function (value, href)
27900     {
27901         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27902         
27903         if(href){
27904             this.el.select('a.small-box-footer',true).first().attr('href', href);
27905         }
27906         
27907     },
27908
27909     setContent: function (value)
27910     {
27911         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27912     },
27913
27914     initEvents: function() 
27915     {   
27916         
27917     }
27918     
27919 });
27920
27921  
27922 /*
27923  * - LGPL
27924  *
27925  * TabBox
27926  * 
27927  */
27928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27929
27930 /**
27931  * @class Roo.bootstrap.dash.TabBox
27932  * @extends Roo.bootstrap.Component
27933  * Bootstrap TabBox class
27934  * @cfg {String} title Title of the TabBox
27935  * @cfg {String} icon Icon of the TabBox
27936  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27937  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27938  * 
27939  * @constructor
27940  * Create a new TabBox
27941  * @param {Object} config The config object
27942  */
27943
27944
27945 Roo.bootstrap.dash.TabBox = function(config){
27946     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27947     this.addEvents({
27948         // raw events
27949         /**
27950          * @event addpane
27951          * When a pane is added
27952          * @param {Roo.bootstrap.dash.TabPane} pane
27953          */
27954         "addpane" : true,
27955         /**
27956          * @event activatepane
27957          * When a pane is activated
27958          * @param {Roo.bootstrap.dash.TabPane} pane
27959          */
27960         "activatepane" : true
27961         
27962          
27963     });
27964     
27965     this.panes = [];
27966 };
27967
27968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27969
27970     title : '',
27971     icon : false,
27972     showtabs : true,
27973     tabScrollable : false,
27974     
27975     getChildContainer : function()
27976     {
27977         return this.el.select('.tab-content', true).first();
27978     },
27979     
27980     getAutoCreate : function(){
27981         
27982         var header = {
27983             tag: 'li',
27984             cls: 'pull-left header',
27985             html: this.title,
27986             cn : []
27987         };
27988         
27989         if(this.icon){
27990             header.cn.push({
27991                 tag: 'i',
27992                 cls: 'fa ' + this.icon
27993             });
27994         }
27995         
27996         var h = {
27997             tag: 'ul',
27998             cls: 'nav nav-tabs pull-right',
27999             cn: [
28000                 header
28001             ]
28002         };
28003         
28004         if(this.tabScrollable){
28005             h = {
28006                 tag: 'div',
28007                 cls: 'tab-header',
28008                 cn: [
28009                     {
28010                         tag: 'ul',
28011                         cls: 'nav nav-tabs pull-right',
28012                         cn: [
28013                             header
28014                         ]
28015                     }
28016                 ]
28017             };
28018         }
28019         
28020         var cfg = {
28021             tag: 'div',
28022             cls: 'nav-tabs-custom',
28023             cn: [
28024                 h,
28025                 {
28026                     tag: 'div',
28027                     cls: 'tab-content no-padding',
28028                     cn: []
28029                 }
28030             ]
28031         };
28032
28033         return  cfg;
28034     },
28035     initEvents : function()
28036     {
28037         //Roo.log('add add pane handler');
28038         this.on('addpane', this.onAddPane, this);
28039     },
28040      /**
28041      * Updates the box title
28042      * @param {String} html to set the title to.
28043      */
28044     setTitle : function(value)
28045     {
28046         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28047     },
28048     onAddPane : function(pane)
28049     {
28050         this.panes.push(pane);
28051         //Roo.log('addpane');
28052         //Roo.log(pane);
28053         // tabs are rendere left to right..
28054         if(!this.showtabs){
28055             return;
28056         }
28057         
28058         var ctr = this.el.select('.nav-tabs', true).first();
28059          
28060          
28061         var existing = ctr.select('.nav-tab',true);
28062         var qty = existing.getCount();;
28063         
28064         
28065         var tab = ctr.createChild({
28066             tag : 'li',
28067             cls : 'nav-tab' + (qty ? '' : ' active'),
28068             cn : [
28069                 {
28070                     tag : 'a',
28071                     href:'#',
28072                     html : pane.title
28073                 }
28074             ]
28075         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28076         pane.tab = tab;
28077         
28078         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28079         if (!qty) {
28080             pane.el.addClass('active');
28081         }
28082         
28083                 
28084     },
28085     onTabClick : function(ev,un,ob,pane)
28086     {
28087         //Roo.log('tab - prev default');
28088         ev.preventDefault();
28089         
28090         
28091         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28092         pane.tab.addClass('active');
28093         //Roo.log(pane.title);
28094         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28095         // technically we should have a deactivate event.. but maybe add later.
28096         // and it should not de-activate the selected tab...
28097         this.fireEvent('activatepane', pane);
28098         pane.el.addClass('active');
28099         pane.fireEvent('activate');
28100         
28101         
28102     },
28103     
28104     getActivePane : function()
28105     {
28106         var r = false;
28107         Roo.each(this.panes, function(p) {
28108             if(p.el.hasClass('active')){
28109                 r = p;
28110                 return false;
28111             }
28112             
28113             return;
28114         });
28115         
28116         return r;
28117     }
28118     
28119     
28120 });
28121
28122  
28123 /*
28124  * - LGPL
28125  *
28126  * Tab pane
28127  * 
28128  */
28129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28130 /**
28131  * @class Roo.bootstrap.TabPane
28132  * @extends Roo.bootstrap.Component
28133  * Bootstrap TabPane class
28134  * @cfg {Boolean} active (false | true) Default false
28135  * @cfg {String} title title of panel
28136
28137  * 
28138  * @constructor
28139  * Create a new TabPane
28140  * @param {Object} config The config object
28141  */
28142
28143 Roo.bootstrap.dash.TabPane = function(config){
28144     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28145     
28146     this.addEvents({
28147         // raw events
28148         /**
28149          * @event activate
28150          * When a pane is activated
28151          * @param {Roo.bootstrap.dash.TabPane} pane
28152          */
28153         "activate" : true
28154          
28155     });
28156 };
28157
28158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28159     
28160     active : false,
28161     title : '',
28162     
28163     // the tabBox that this is attached to.
28164     tab : false,
28165      
28166     getAutoCreate : function() 
28167     {
28168         var cfg = {
28169             tag: 'div',
28170             cls: 'tab-pane'
28171         };
28172         
28173         if(this.active){
28174             cfg.cls += ' active';
28175         }
28176         
28177         return cfg;
28178     },
28179     initEvents  : function()
28180     {
28181         //Roo.log('trigger add pane handler');
28182         this.parent().fireEvent('addpane', this)
28183     },
28184     
28185      /**
28186      * Updates the tab title 
28187      * @param {String} html to set the title to.
28188      */
28189     setTitle: function(str)
28190     {
28191         if (!this.tab) {
28192             return;
28193         }
28194         this.title = str;
28195         this.tab.select('a', true).first().dom.innerHTML = str;
28196         
28197     }
28198     
28199     
28200     
28201 });
28202
28203  
28204
28205
28206  /*
28207  * - LGPL
28208  *
28209  * menu
28210  * 
28211  */
28212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28213
28214 /**
28215  * @class Roo.bootstrap.menu.Menu
28216  * @extends Roo.bootstrap.Component
28217  * Bootstrap Menu class - container for Menu
28218  * @cfg {String} html Text of the menu
28219  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28220  * @cfg {String} icon Font awesome icon
28221  * @cfg {String} pos Menu align to (top | bottom) default bottom
28222  * 
28223  * 
28224  * @constructor
28225  * Create a new Menu
28226  * @param {Object} config The config object
28227  */
28228
28229
28230 Roo.bootstrap.menu.Menu = function(config){
28231     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28232     
28233     this.addEvents({
28234         /**
28235          * @event beforeshow
28236          * Fires before this menu is displayed
28237          * @param {Roo.bootstrap.menu.Menu} this
28238          */
28239         beforeshow : true,
28240         /**
28241          * @event beforehide
28242          * Fires before this menu is hidden
28243          * @param {Roo.bootstrap.menu.Menu} this
28244          */
28245         beforehide : true,
28246         /**
28247          * @event show
28248          * Fires after this menu is displayed
28249          * @param {Roo.bootstrap.menu.Menu} this
28250          */
28251         show : true,
28252         /**
28253          * @event hide
28254          * Fires after this menu is hidden
28255          * @param {Roo.bootstrap.menu.Menu} this
28256          */
28257         hide : true,
28258         /**
28259          * @event click
28260          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28261          * @param {Roo.bootstrap.menu.Menu} this
28262          * @param {Roo.EventObject} e
28263          */
28264         click : true
28265     });
28266     
28267 };
28268
28269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28270     
28271     submenu : false,
28272     html : '',
28273     weight : 'default',
28274     icon : false,
28275     pos : 'bottom',
28276     
28277     
28278     getChildContainer : function() {
28279         if(this.isSubMenu){
28280             return this.el;
28281         }
28282         
28283         return this.el.select('ul.dropdown-menu', true).first();  
28284     },
28285     
28286     getAutoCreate : function()
28287     {
28288         var text = [
28289             {
28290                 tag : 'span',
28291                 cls : 'roo-menu-text',
28292                 html : this.html
28293             }
28294         ];
28295         
28296         if(this.icon){
28297             text.unshift({
28298                 tag : 'i',
28299                 cls : 'fa ' + this.icon
28300             })
28301         }
28302         
28303         
28304         var cfg = {
28305             tag : 'div',
28306             cls : 'btn-group',
28307             cn : [
28308                 {
28309                     tag : 'button',
28310                     cls : 'dropdown-button btn btn-' + this.weight,
28311                     cn : text
28312                 },
28313                 {
28314                     tag : 'button',
28315                     cls : 'dropdown-toggle btn btn-' + this.weight,
28316                     cn : [
28317                         {
28318                             tag : 'span',
28319                             cls : 'caret'
28320                         }
28321                     ]
28322                 },
28323                 {
28324                     tag : 'ul',
28325                     cls : 'dropdown-menu'
28326                 }
28327             ]
28328             
28329         };
28330         
28331         if(this.pos == 'top'){
28332             cfg.cls += ' dropup';
28333         }
28334         
28335         if(this.isSubMenu){
28336             cfg = {
28337                 tag : 'ul',
28338                 cls : 'dropdown-menu'
28339             }
28340         }
28341         
28342         return cfg;
28343     },
28344     
28345     onRender : function(ct, position)
28346     {
28347         this.isSubMenu = ct.hasClass('dropdown-submenu');
28348         
28349         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28350     },
28351     
28352     initEvents : function() 
28353     {
28354         if(this.isSubMenu){
28355             return;
28356         }
28357         
28358         this.hidden = true;
28359         
28360         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28361         this.triggerEl.on('click', this.onTriggerPress, this);
28362         
28363         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28364         this.buttonEl.on('click', this.onClick, this);
28365         
28366     },
28367     
28368     list : function()
28369     {
28370         if(this.isSubMenu){
28371             return this.el;
28372         }
28373         
28374         return this.el.select('ul.dropdown-menu', true).first();
28375     },
28376     
28377     onClick : function(e)
28378     {
28379         this.fireEvent("click", this, e);
28380     },
28381     
28382     onTriggerPress  : function(e)
28383     {   
28384         if (this.isVisible()) {
28385             this.hide();
28386         } else {
28387             this.show();
28388         }
28389     },
28390     
28391     isVisible : function(){
28392         return !this.hidden;
28393     },
28394     
28395     show : function()
28396     {
28397         this.fireEvent("beforeshow", this);
28398         
28399         this.hidden = false;
28400         this.el.addClass('open');
28401         
28402         Roo.get(document).on("mouseup", this.onMouseUp, this);
28403         
28404         this.fireEvent("show", this);
28405         
28406         
28407     },
28408     
28409     hide : function()
28410     {
28411         this.fireEvent("beforehide", this);
28412         
28413         this.hidden = true;
28414         this.el.removeClass('open');
28415         
28416         Roo.get(document).un("mouseup", this.onMouseUp);
28417         
28418         this.fireEvent("hide", this);
28419     },
28420     
28421     onMouseUp : function()
28422     {
28423         this.hide();
28424     }
28425     
28426 });
28427
28428  
28429  /*
28430  * - LGPL
28431  *
28432  * menu item
28433  * 
28434  */
28435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28436
28437 /**
28438  * @class Roo.bootstrap.menu.Item
28439  * @extends Roo.bootstrap.Component
28440  * Bootstrap MenuItem class
28441  * @cfg {Boolean} submenu (true | false) default false
28442  * @cfg {String} html text of the item
28443  * @cfg {String} href the link
28444  * @cfg {Boolean} disable (true | false) default false
28445  * @cfg {Boolean} preventDefault (true | false) default true
28446  * @cfg {String} icon Font awesome icon
28447  * @cfg {String} pos Submenu align to (left | right) default right 
28448  * 
28449  * 
28450  * @constructor
28451  * Create a new Item
28452  * @param {Object} config The config object
28453  */
28454
28455
28456 Roo.bootstrap.menu.Item = function(config){
28457     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28458     this.addEvents({
28459         /**
28460          * @event mouseover
28461          * Fires when the mouse is hovering over this menu
28462          * @param {Roo.bootstrap.menu.Item} this
28463          * @param {Roo.EventObject} e
28464          */
28465         mouseover : true,
28466         /**
28467          * @event mouseout
28468          * Fires when the mouse exits this menu
28469          * @param {Roo.bootstrap.menu.Item} this
28470          * @param {Roo.EventObject} e
28471          */
28472         mouseout : true,
28473         // raw events
28474         /**
28475          * @event click
28476          * The raw click event for the entire grid.
28477          * @param {Roo.EventObject} e
28478          */
28479         click : true
28480     });
28481 };
28482
28483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28484     
28485     submenu : false,
28486     href : '',
28487     html : '',
28488     preventDefault: true,
28489     disable : false,
28490     icon : false,
28491     pos : 'right',
28492     
28493     getAutoCreate : function()
28494     {
28495         var text = [
28496             {
28497                 tag : 'span',
28498                 cls : 'roo-menu-item-text',
28499                 html : this.html
28500             }
28501         ];
28502         
28503         if(this.icon){
28504             text.unshift({
28505                 tag : 'i',
28506                 cls : 'fa ' + this.icon
28507             })
28508         }
28509         
28510         var cfg = {
28511             tag : 'li',
28512             cn : [
28513                 {
28514                     tag : 'a',
28515                     href : this.href || '#',
28516                     cn : text
28517                 }
28518             ]
28519         };
28520         
28521         if(this.disable){
28522             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28523         }
28524         
28525         if(this.submenu){
28526             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28527             
28528             if(this.pos == 'left'){
28529                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28530             }
28531         }
28532         
28533         return cfg;
28534     },
28535     
28536     initEvents : function() 
28537     {
28538         this.el.on('mouseover', this.onMouseOver, this);
28539         this.el.on('mouseout', this.onMouseOut, this);
28540         
28541         this.el.select('a', true).first().on('click', this.onClick, this);
28542         
28543     },
28544     
28545     onClick : function(e)
28546     {
28547         if(this.preventDefault){
28548             e.preventDefault();
28549         }
28550         
28551         this.fireEvent("click", this, e);
28552     },
28553     
28554     onMouseOver : function(e)
28555     {
28556         if(this.submenu && this.pos == 'left'){
28557             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28558         }
28559         
28560         this.fireEvent("mouseover", this, e);
28561     },
28562     
28563     onMouseOut : function(e)
28564     {
28565         this.fireEvent("mouseout", this, e);
28566     }
28567 });
28568
28569  
28570
28571  /*
28572  * - LGPL
28573  *
28574  * menu separator
28575  * 
28576  */
28577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28578
28579 /**
28580  * @class Roo.bootstrap.menu.Separator
28581  * @extends Roo.bootstrap.Component
28582  * Bootstrap Separator class
28583  * 
28584  * @constructor
28585  * Create a new Separator
28586  * @param {Object} config The config object
28587  */
28588
28589
28590 Roo.bootstrap.menu.Separator = function(config){
28591     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28592 };
28593
28594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28595     
28596     getAutoCreate : function(){
28597         var cfg = {
28598             tag : 'li',
28599             cls: 'divider'
28600         };
28601         
28602         return cfg;
28603     }
28604    
28605 });
28606
28607  
28608
28609  /*
28610  * - LGPL
28611  *
28612  * Tooltip
28613  * 
28614  */
28615
28616 /**
28617  * @class Roo.bootstrap.Tooltip
28618  * Bootstrap Tooltip class
28619  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28620  * to determine which dom element triggers the tooltip.
28621  * 
28622  * It needs to add support for additional attributes like tooltip-position
28623  * 
28624  * @constructor
28625  * Create a new Toolti
28626  * @param {Object} config The config object
28627  */
28628
28629 Roo.bootstrap.Tooltip = function(config){
28630     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28631     
28632     this.alignment = Roo.bootstrap.Tooltip.alignment;
28633     
28634     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28635         this.alignment = config.alignment;
28636     }
28637     
28638 };
28639
28640 Roo.apply(Roo.bootstrap.Tooltip, {
28641     /**
28642      * @function init initialize tooltip monitoring.
28643      * @static
28644      */
28645     currentEl : false,
28646     currentTip : false,
28647     currentRegion : false,
28648     
28649     //  init : delay?
28650     
28651     init : function()
28652     {
28653         Roo.get(document).on('mouseover', this.enter ,this);
28654         Roo.get(document).on('mouseout', this.leave, this);
28655          
28656         
28657         this.currentTip = new Roo.bootstrap.Tooltip();
28658     },
28659     
28660     enter : function(ev)
28661     {
28662         var dom = ev.getTarget();
28663         
28664         //Roo.log(['enter',dom]);
28665         var el = Roo.fly(dom);
28666         if (this.currentEl) {
28667             //Roo.log(dom);
28668             //Roo.log(this.currentEl);
28669             //Roo.log(this.currentEl.contains(dom));
28670             if (this.currentEl == el) {
28671                 return;
28672             }
28673             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28674                 return;
28675             }
28676
28677         }
28678         
28679         if (this.currentTip.el) {
28680             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28681         }    
28682         //Roo.log(ev);
28683         
28684         if(!el || el.dom == document){
28685             return;
28686         }
28687         
28688         var bindEl = el;
28689         
28690         // you can not look for children, as if el is the body.. then everythign is the child..
28691         if (!el.attr('tooltip')) { //
28692             if (!el.select("[tooltip]").elements.length) {
28693                 return;
28694             }
28695             // is the mouse over this child...?
28696             bindEl = el.select("[tooltip]").first();
28697             var xy = ev.getXY();
28698             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28699                 //Roo.log("not in region.");
28700                 return;
28701             }
28702             //Roo.log("child element over..");
28703             
28704         }
28705         this.currentEl = bindEl;
28706         this.currentTip.bind(bindEl);
28707         this.currentRegion = Roo.lib.Region.getRegion(dom);
28708         this.currentTip.enter();
28709         
28710     },
28711     leave : function(ev)
28712     {
28713         var dom = ev.getTarget();
28714         //Roo.log(['leave',dom]);
28715         if (!this.currentEl) {
28716             return;
28717         }
28718         
28719         
28720         if (dom != this.currentEl.dom) {
28721             return;
28722         }
28723         var xy = ev.getXY();
28724         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28725             return;
28726         }
28727         // only activate leave if mouse cursor is outside... bounding box..
28728         
28729         
28730         
28731         
28732         if (this.currentTip) {
28733             this.currentTip.leave();
28734         }
28735         //Roo.log('clear currentEl');
28736         this.currentEl = false;
28737         
28738         
28739     },
28740     alignment : {
28741         'left' : ['r-l', [-2,0], 'right'],
28742         'right' : ['l-r', [2,0], 'left'],
28743         'bottom' : ['t-b', [0,2], 'top'],
28744         'top' : [ 'b-t', [0,-2], 'bottom']
28745     }
28746     
28747 });
28748
28749
28750 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28751     
28752     
28753     bindEl : false,
28754     
28755     delay : null, // can be { show : 300 , hide: 500}
28756     
28757     timeout : null,
28758     
28759     hoverState : null, //???
28760     
28761     placement : 'bottom', 
28762     
28763     alignment : false,
28764     
28765     getAutoCreate : function(){
28766     
28767         var cfg = {
28768            cls : 'tooltip',   
28769            role : 'tooltip',
28770            cn : [
28771                 {
28772                     cls : 'tooltip-arrow arrow'
28773                 },
28774                 {
28775                     cls : 'tooltip-inner'
28776                 }
28777            ]
28778         };
28779         
28780         return cfg;
28781     },
28782     bind : function(el)
28783     {
28784         this.bindEl = el;
28785     },
28786     
28787     initEvents : function()
28788     {
28789         this.arrowEl = this.el.select('.arrow', true).first();
28790         this.innerEl = this.el.select('.tooltip-inner', true).first();
28791     },
28792     
28793     enter : function () {
28794        
28795         if (this.timeout != null) {
28796             clearTimeout(this.timeout);
28797         }
28798         
28799         this.hoverState = 'in';
28800          //Roo.log("enter - show");
28801         if (!this.delay || !this.delay.show) {
28802             this.show();
28803             return;
28804         }
28805         var _t = this;
28806         this.timeout = setTimeout(function () {
28807             if (_t.hoverState == 'in') {
28808                 _t.show();
28809             }
28810         }, this.delay.show);
28811     },
28812     leave : function()
28813     {
28814         clearTimeout(this.timeout);
28815     
28816         this.hoverState = 'out';
28817          if (!this.delay || !this.delay.hide) {
28818             this.hide();
28819             return;
28820         }
28821        
28822         var _t = this;
28823         this.timeout = setTimeout(function () {
28824             //Roo.log("leave - timeout");
28825             
28826             if (_t.hoverState == 'out') {
28827                 _t.hide();
28828                 Roo.bootstrap.Tooltip.currentEl = false;
28829             }
28830         }, delay);
28831     },
28832     
28833     show : function (msg)
28834     {
28835         if (!this.el) {
28836             this.render(document.body);
28837         }
28838         // set content.
28839         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28840         
28841         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28842         
28843         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28844         
28845         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28846                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28847         
28848         var placement = typeof this.placement == 'function' ?
28849             this.placement.call(this, this.el, on_el) :
28850             this.placement;
28851             
28852         var autoToken = /\s?auto?\s?/i;
28853         var autoPlace = autoToken.test(placement);
28854         if (autoPlace) {
28855             placement = placement.replace(autoToken, '') || 'top';
28856         }
28857         
28858         //this.el.detach()
28859         //this.el.setXY([0,0]);
28860         this.el.show();
28861         //this.el.dom.style.display='block';
28862         
28863         //this.el.appendTo(on_el);
28864         
28865         var p = this.getPosition();
28866         var box = this.el.getBox();
28867         
28868         if (autoPlace) {
28869             // fixme..
28870         }
28871         
28872         var align = this.alignment[placement];
28873         
28874         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28875         
28876         if(placement == 'top' || placement == 'bottom'){
28877             if(xy[0] < 0){
28878                 placement = 'right';
28879             }
28880             
28881             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28882                 placement = 'left';
28883             }
28884             
28885             var scroll = Roo.select('body', true).first().getScroll();
28886             
28887             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28888                 placement = 'top';
28889             }
28890             
28891             align = this.alignment[placement];
28892             
28893             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28894             
28895         }
28896         
28897         this.el.alignTo(this.bindEl, align[0],align[1]);
28898         //var arrow = this.el.select('.arrow',true).first();
28899         //arrow.set(align[2], 
28900         
28901         this.el.addClass(placement);
28902         this.el.addClass("bs-tooltip-"+ placement);
28903         
28904         this.el.addClass('in fade show');
28905         
28906         this.hoverState = null;
28907         
28908         if (this.el.hasClass('fade')) {
28909             // fade it?
28910         }
28911         
28912         
28913         
28914         
28915         
28916     },
28917     hide : function()
28918     {
28919          
28920         if (!this.el) {
28921             return;
28922         }
28923         //this.el.setXY([0,0]);
28924         this.el.removeClass(['show', 'in']);
28925         //this.el.hide();
28926         
28927     }
28928     
28929 });
28930  
28931
28932  /*
28933  * - LGPL
28934  *
28935  * Location Picker
28936  * 
28937  */
28938
28939 /**
28940  * @class Roo.bootstrap.LocationPicker
28941  * @extends Roo.bootstrap.Component
28942  * Bootstrap LocationPicker class
28943  * @cfg {Number} latitude Position when init default 0
28944  * @cfg {Number} longitude Position when init default 0
28945  * @cfg {Number} zoom default 15
28946  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28947  * @cfg {Boolean} mapTypeControl default false
28948  * @cfg {Boolean} disableDoubleClickZoom default false
28949  * @cfg {Boolean} scrollwheel default true
28950  * @cfg {Boolean} streetViewControl default false
28951  * @cfg {Number} radius default 0
28952  * @cfg {String} locationName
28953  * @cfg {Boolean} draggable default true
28954  * @cfg {Boolean} enableAutocomplete default false
28955  * @cfg {Boolean} enableReverseGeocode default true
28956  * @cfg {String} markerTitle
28957  * 
28958  * @constructor
28959  * Create a new LocationPicker
28960  * @param {Object} config The config object
28961  */
28962
28963
28964 Roo.bootstrap.LocationPicker = function(config){
28965     
28966     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28967     
28968     this.addEvents({
28969         /**
28970          * @event initial
28971          * Fires when the picker initialized.
28972          * @param {Roo.bootstrap.LocationPicker} this
28973          * @param {Google Location} location
28974          */
28975         initial : true,
28976         /**
28977          * @event positionchanged
28978          * Fires when the picker position changed.
28979          * @param {Roo.bootstrap.LocationPicker} this
28980          * @param {Google Location} location
28981          */
28982         positionchanged : true,
28983         /**
28984          * @event resize
28985          * Fires when the map resize.
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          */
28988         resize : true,
28989         /**
28990          * @event show
28991          * Fires when the map show.
28992          * @param {Roo.bootstrap.LocationPicker} this
28993          */
28994         show : true,
28995         /**
28996          * @event hide
28997          * Fires when the map hide.
28998          * @param {Roo.bootstrap.LocationPicker} this
28999          */
29000         hide : true,
29001         /**
29002          * @event mapClick
29003          * Fires when click the map.
29004          * @param {Roo.bootstrap.LocationPicker} this
29005          * @param {Map event} e
29006          */
29007         mapClick : true,
29008         /**
29009          * @event mapRightClick
29010          * Fires when right click the map.
29011          * @param {Roo.bootstrap.LocationPicker} this
29012          * @param {Map event} e
29013          */
29014         mapRightClick : true,
29015         /**
29016          * @event markerClick
29017          * Fires when click the marker.
29018          * @param {Roo.bootstrap.LocationPicker} this
29019          * @param {Map event} e
29020          */
29021         markerClick : true,
29022         /**
29023          * @event markerRightClick
29024          * Fires when right click the marker.
29025          * @param {Roo.bootstrap.LocationPicker} this
29026          * @param {Map event} e
29027          */
29028         markerRightClick : true,
29029         /**
29030          * @event OverlayViewDraw
29031          * Fires when OverlayView Draw
29032          * @param {Roo.bootstrap.LocationPicker} this
29033          */
29034         OverlayViewDraw : true,
29035         /**
29036          * @event OverlayViewOnAdd
29037          * Fires when OverlayView Draw
29038          * @param {Roo.bootstrap.LocationPicker} this
29039          */
29040         OverlayViewOnAdd : true,
29041         /**
29042          * @event OverlayViewOnRemove
29043          * Fires when OverlayView Draw
29044          * @param {Roo.bootstrap.LocationPicker} this
29045          */
29046         OverlayViewOnRemove : true,
29047         /**
29048          * @event OverlayViewShow
29049          * Fires when OverlayView Draw
29050          * @param {Roo.bootstrap.LocationPicker} this
29051          * @param {Pixel} cpx
29052          */
29053         OverlayViewShow : true,
29054         /**
29055          * @event OverlayViewHide
29056          * Fires when OverlayView Draw
29057          * @param {Roo.bootstrap.LocationPicker} this
29058          */
29059         OverlayViewHide : true,
29060         /**
29061          * @event loadexception
29062          * Fires when load google lib failed.
29063          * @param {Roo.bootstrap.LocationPicker} this
29064          */
29065         loadexception : true
29066     });
29067         
29068 };
29069
29070 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29071     
29072     gMapContext: false,
29073     
29074     latitude: 0,
29075     longitude: 0,
29076     zoom: 15,
29077     mapTypeId: false,
29078     mapTypeControl: false,
29079     disableDoubleClickZoom: false,
29080     scrollwheel: true,
29081     streetViewControl: false,
29082     radius: 0,
29083     locationName: '',
29084     draggable: true,
29085     enableAutocomplete: false,
29086     enableReverseGeocode: true,
29087     markerTitle: '',
29088     
29089     getAutoCreate: function()
29090     {
29091
29092         var cfg = {
29093             tag: 'div',
29094             cls: 'roo-location-picker'
29095         };
29096         
29097         return cfg
29098     },
29099     
29100     initEvents: function(ct, position)
29101     {       
29102         if(!this.el.getWidth() || this.isApplied()){
29103             return;
29104         }
29105         
29106         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29107         
29108         this.initial();
29109     },
29110     
29111     initial: function()
29112     {
29113         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29114             this.fireEvent('loadexception', this);
29115             return;
29116         }
29117         
29118         if(!this.mapTypeId){
29119             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29120         }
29121         
29122         this.gMapContext = this.GMapContext();
29123         
29124         this.initOverlayView();
29125         
29126         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29127         
29128         var _this = this;
29129                 
29130         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29131             _this.setPosition(_this.gMapContext.marker.position);
29132         });
29133         
29134         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29135             _this.fireEvent('mapClick', this, event);
29136             
29137         });
29138
29139         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29140             _this.fireEvent('mapRightClick', this, event);
29141             
29142         });
29143         
29144         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29145             _this.fireEvent('markerClick', this, event);
29146             
29147         });
29148
29149         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29150             _this.fireEvent('markerRightClick', this, event);
29151             
29152         });
29153         
29154         this.setPosition(this.gMapContext.location);
29155         
29156         this.fireEvent('initial', this, this.gMapContext.location);
29157     },
29158     
29159     initOverlayView: function()
29160     {
29161         var _this = this;
29162         
29163         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29164             
29165             draw: function()
29166             {
29167                 _this.fireEvent('OverlayViewDraw', _this);
29168             },
29169             
29170             onAdd: function()
29171             {
29172                 _this.fireEvent('OverlayViewOnAdd', _this);
29173             },
29174             
29175             onRemove: function()
29176             {
29177                 _this.fireEvent('OverlayViewOnRemove', _this);
29178             },
29179             
29180             show: function(cpx)
29181             {
29182                 _this.fireEvent('OverlayViewShow', _this, cpx);
29183             },
29184             
29185             hide: function()
29186             {
29187                 _this.fireEvent('OverlayViewHide', _this);
29188             }
29189             
29190         });
29191     },
29192     
29193     fromLatLngToContainerPixel: function(event)
29194     {
29195         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29196     },
29197     
29198     isApplied: function() 
29199     {
29200         return this.getGmapContext() == false ? false : true;
29201     },
29202     
29203     getGmapContext: function() 
29204     {
29205         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29206     },
29207     
29208     GMapContext: function() 
29209     {
29210         var position = new google.maps.LatLng(this.latitude, this.longitude);
29211         
29212         var _map = new google.maps.Map(this.el.dom, {
29213             center: position,
29214             zoom: this.zoom,
29215             mapTypeId: this.mapTypeId,
29216             mapTypeControl: this.mapTypeControl,
29217             disableDoubleClickZoom: this.disableDoubleClickZoom,
29218             scrollwheel: this.scrollwheel,
29219             streetViewControl: this.streetViewControl,
29220             locationName: this.locationName,
29221             draggable: this.draggable,
29222             enableAutocomplete: this.enableAutocomplete,
29223             enableReverseGeocode: this.enableReverseGeocode
29224         });
29225         
29226         var _marker = new google.maps.Marker({
29227             position: position,
29228             map: _map,
29229             title: this.markerTitle,
29230             draggable: this.draggable
29231         });
29232         
29233         return {
29234             map: _map,
29235             marker: _marker,
29236             circle: null,
29237             location: position,
29238             radius: this.radius,
29239             locationName: this.locationName,
29240             addressComponents: {
29241                 formatted_address: null,
29242                 addressLine1: null,
29243                 addressLine2: null,
29244                 streetName: null,
29245                 streetNumber: null,
29246                 city: null,
29247                 district: null,
29248                 state: null,
29249                 stateOrProvince: null
29250             },
29251             settings: this,
29252             domContainer: this.el.dom,
29253             geodecoder: new google.maps.Geocoder()
29254         };
29255     },
29256     
29257     drawCircle: function(center, radius, options) 
29258     {
29259         if (this.gMapContext.circle != null) {
29260             this.gMapContext.circle.setMap(null);
29261         }
29262         if (radius > 0) {
29263             radius *= 1;
29264             options = Roo.apply({}, options, {
29265                 strokeColor: "#0000FF",
29266                 strokeOpacity: .35,
29267                 strokeWeight: 2,
29268                 fillColor: "#0000FF",
29269                 fillOpacity: .2
29270             });
29271             
29272             options.map = this.gMapContext.map;
29273             options.radius = radius;
29274             options.center = center;
29275             this.gMapContext.circle = new google.maps.Circle(options);
29276             return this.gMapContext.circle;
29277         }
29278         
29279         return null;
29280     },
29281     
29282     setPosition: function(location) 
29283     {
29284         this.gMapContext.location = location;
29285         this.gMapContext.marker.setPosition(location);
29286         this.gMapContext.map.panTo(location);
29287         this.drawCircle(location, this.gMapContext.radius, {});
29288         
29289         var _this = this;
29290         
29291         if (this.gMapContext.settings.enableReverseGeocode) {
29292             this.gMapContext.geodecoder.geocode({
29293                 latLng: this.gMapContext.location
29294             }, function(results, status) {
29295                 
29296                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29297                     _this.gMapContext.locationName = results[0].formatted_address;
29298                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29299                     
29300                     _this.fireEvent('positionchanged', this, location);
29301                 }
29302             });
29303             
29304             return;
29305         }
29306         
29307         this.fireEvent('positionchanged', this, location);
29308     },
29309     
29310     resize: function()
29311     {
29312         google.maps.event.trigger(this.gMapContext.map, "resize");
29313         
29314         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29315         
29316         this.fireEvent('resize', this);
29317     },
29318     
29319     setPositionByLatLng: function(latitude, longitude)
29320     {
29321         this.setPosition(new google.maps.LatLng(latitude, longitude));
29322     },
29323     
29324     getCurrentPosition: function() 
29325     {
29326         return {
29327             latitude: this.gMapContext.location.lat(),
29328             longitude: this.gMapContext.location.lng()
29329         };
29330     },
29331     
29332     getAddressName: function() 
29333     {
29334         return this.gMapContext.locationName;
29335     },
29336     
29337     getAddressComponents: function() 
29338     {
29339         return this.gMapContext.addressComponents;
29340     },
29341     
29342     address_component_from_google_geocode: function(address_components) 
29343     {
29344         var result = {};
29345         
29346         for (var i = 0; i < address_components.length; i++) {
29347             var component = address_components[i];
29348             if (component.types.indexOf("postal_code") >= 0) {
29349                 result.postalCode = component.short_name;
29350             } else if (component.types.indexOf("street_number") >= 0) {
29351                 result.streetNumber = component.short_name;
29352             } else if (component.types.indexOf("route") >= 0) {
29353                 result.streetName = component.short_name;
29354             } else if (component.types.indexOf("neighborhood") >= 0) {
29355                 result.city = component.short_name;
29356             } else if (component.types.indexOf("locality") >= 0) {
29357                 result.city = component.short_name;
29358             } else if (component.types.indexOf("sublocality") >= 0) {
29359                 result.district = component.short_name;
29360             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29361                 result.stateOrProvince = component.short_name;
29362             } else if (component.types.indexOf("country") >= 0) {
29363                 result.country = component.short_name;
29364             }
29365         }
29366         
29367         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29368         result.addressLine2 = "";
29369         return result;
29370     },
29371     
29372     setZoomLevel: function(zoom)
29373     {
29374         this.gMapContext.map.setZoom(zoom);
29375     },
29376     
29377     show: function()
29378     {
29379         if(!this.el){
29380             return;
29381         }
29382         
29383         this.el.show();
29384         
29385         this.resize();
29386         
29387         this.fireEvent('show', this);
29388     },
29389     
29390     hide: function()
29391     {
29392         if(!this.el){
29393             return;
29394         }
29395         
29396         this.el.hide();
29397         
29398         this.fireEvent('hide', this);
29399     }
29400     
29401 });
29402
29403 Roo.apply(Roo.bootstrap.LocationPicker, {
29404     
29405     OverlayView : function(map, options)
29406     {
29407         options = options || {};
29408         
29409         this.setMap(map);
29410     }
29411     
29412     
29413 });/**
29414  * @class Roo.bootstrap.Alert
29415  * @extends Roo.bootstrap.Component
29416  * Bootstrap Alert class - shows an alert area box
29417  * eg
29418  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29419   Enter a valid email address
29420 </div>
29421  * @licence LGPL
29422  * @cfg {String} title The title of alert
29423  * @cfg {String} html The content of alert
29424  * @cfg {String} weight (  success | info | warning | danger )
29425  * @cfg {String} faicon font-awesomeicon
29426  * 
29427  * @constructor
29428  * Create a new alert
29429  * @param {Object} config The config object
29430  */
29431
29432
29433 Roo.bootstrap.Alert = function(config){
29434     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29435     
29436 };
29437
29438 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29439     
29440     title: '',
29441     html: '',
29442     weight: false,
29443     faicon: false,
29444     
29445     getAutoCreate : function()
29446     {
29447         
29448         var cfg = {
29449             tag : 'div',
29450             cls : 'alert',
29451             cn : [
29452                 {
29453                     tag : 'i',
29454                     cls : 'roo-alert-icon'
29455                     
29456                 },
29457                 {
29458                     tag : 'b',
29459                     cls : 'roo-alert-title',
29460                     html : this.title
29461                 },
29462                 {
29463                     tag : 'span',
29464                     cls : 'roo-alert-text',
29465                     html : this.html
29466                 }
29467             ]
29468         };
29469         
29470         if(this.faicon){
29471             cfg.cn[0].cls += ' fa ' + this.faicon;
29472         }
29473         
29474         if(this.weight){
29475             cfg.cls += ' alert-' + this.weight;
29476         }
29477         
29478         return cfg;
29479     },
29480     
29481     initEvents: function() 
29482     {
29483         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29484     },
29485     
29486     setTitle : function(str)
29487     {
29488         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29489     },
29490     
29491     setText : function(str)
29492     {
29493         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29494     },
29495     
29496     setWeight : function(weight)
29497     {
29498         if(this.weight){
29499             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29500         }
29501         
29502         this.weight = weight;
29503         
29504         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29505     },
29506     
29507     setIcon : function(icon)
29508     {
29509         if(this.faicon){
29510             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29511         }
29512         
29513         this.faicon = icon;
29514         
29515         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29516     },
29517     
29518     hide: function() 
29519     {
29520         this.el.hide();   
29521     },
29522     
29523     show: function() 
29524     {  
29525         this.el.show();   
29526     }
29527     
29528 });
29529
29530  
29531 /*
29532 * Licence: LGPL
29533 */
29534
29535 /**
29536  * @class Roo.bootstrap.UploadCropbox
29537  * @extends Roo.bootstrap.Component
29538  * Bootstrap UploadCropbox class
29539  * @cfg {String} emptyText show when image has been loaded
29540  * @cfg {String} rotateNotify show when image too small to rotate
29541  * @cfg {Number} errorTimeout default 3000
29542  * @cfg {Number} minWidth default 300
29543  * @cfg {Number} minHeight default 300
29544  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29545  * @cfg {Boolean} isDocument (true|false) default false
29546  * @cfg {String} url action url
29547  * @cfg {String} paramName default 'imageUpload'
29548  * @cfg {String} method default POST
29549  * @cfg {Boolean} loadMask (true|false) default true
29550  * @cfg {Boolean} loadingText default 'Loading...'
29551  * 
29552  * @constructor
29553  * Create a new UploadCropbox
29554  * @param {Object} config The config object
29555  */
29556
29557 Roo.bootstrap.UploadCropbox = function(config){
29558     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29559     
29560     this.addEvents({
29561         /**
29562          * @event beforeselectfile
29563          * Fire before select file
29564          * @param {Roo.bootstrap.UploadCropbox} this
29565          */
29566         "beforeselectfile" : true,
29567         /**
29568          * @event initial
29569          * Fire after initEvent
29570          * @param {Roo.bootstrap.UploadCropbox} this
29571          */
29572         "initial" : true,
29573         /**
29574          * @event crop
29575          * Fire after initEvent
29576          * @param {Roo.bootstrap.UploadCropbox} this
29577          * @param {String} data
29578          */
29579         "crop" : true,
29580         /**
29581          * @event prepare
29582          * Fire when preparing the file data
29583          * @param {Roo.bootstrap.UploadCropbox} this
29584          * @param {Object} file
29585          */
29586         "prepare" : true,
29587         /**
29588          * @event exception
29589          * Fire when get exception
29590          * @param {Roo.bootstrap.UploadCropbox} this
29591          * @param {XMLHttpRequest} xhr
29592          */
29593         "exception" : true,
29594         /**
29595          * @event beforeloadcanvas
29596          * Fire before load the canvas
29597          * @param {Roo.bootstrap.UploadCropbox} this
29598          * @param {String} src
29599          */
29600         "beforeloadcanvas" : true,
29601         /**
29602          * @event trash
29603          * Fire when trash image
29604          * @param {Roo.bootstrap.UploadCropbox} this
29605          */
29606         "trash" : true,
29607         /**
29608          * @event download
29609          * Fire when download the image
29610          * @param {Roo.bootstrap.UploadCropbox} this
29611          */
29612         "download" : true,
29613         /**
29614          * @event footerbuttonclick
29615          * Fire when footerbuttonclick
29616          * @param {Roo.bootstrap.UploadCropbox} this
29617          * @param {String} type
29618          */
29619         "footerbuttonclick" : true,
29620         /**
29621          * @event resize
29622          * Fire when resize
29623          * @param {Roo.bootstrap.UploadCropbox} this
29624          */
29625         "resize" : true,
29626         /**
29627          * @event rotate
29628          * Fire when rotate the image
29629          * @param {Roo.bootstrap.UploadCropbox} this
29630          * @param {String} pos
29631          */
29632         "rotate" : true,
29633         /**
29634          * @event inspect
29635          * Fire when inspect the file
29636          * @param {Roo.bootstrap.UploadCropbox} this
29637          * @param {Object} file
29638          */
29639         "inspect" : true,
29640         /**
29641          * @event upload
29642          * Fire when xhr upload the file
29643          * @param {Roo.bootstrap.UploadCropbox} this
29644          * @param {Object} data
29645          */
29646         "upload" : true,
29647         /**
29648          * @event arrange
29649          * Fire when arrange the file data
29650          * @param {Roo.bootstrap.UploadCropbox} this
29651          * @param {Object} formData
29652          */
29653         "arrange" : true
29654     });
29655     
29656     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29657 };
29658
29659 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29660     
29661     emptyText : 'Click to upload image',
29662     rotateNotify : 'Image is too small to rotate',
29663     errorTimeout : 3000,
29664     scale : 0,
29665     baseScale : 1,
29666     rotate : 0,
29667     dragable : false,
29668     pinching : false,
29669     mouseX : 0,
29670     mouseY : 0,
29671     cropData : false,
29672     minWidth : 300,
29673     minHeight : 300,
29674     file : false,
29675     exif : {},
29676     baseRotate : 1,
29677     cropType : 'image/jpeg',
29678     buttons : false,
29679     canvasLoaded : false,
29680     isDocument : false,
29681     method : 'POST',
29682     paramName : 'imageUpload',
29683     loadMask : true,
29684     loadingText : 'Loading...',
29685     maskEl : false,
29686     
29687     getAutoCreate : function()
29688     {
29689         var cfg = {
29690             tag : 'div',
29691             cls : 'roo-upload-cropbox',
29692             cn : [
29693                 {
29694                     tag : 'input',
29695                     cls : 'roo-upload-cropbox-selector',
29696                     type : 'file'
29697                 },
29698                 {
29699                     tag : 'div',
29700                     cls : 'roo-upload-cropbox-body',
29701                     style : 'cursor:pointer',
29702                     cn : [
29703                         {
29704                             tag : 'div',
29705                             cls : 'roo-upload-cropbox-preview'
29706                         },
29707                         {
29708                             tag : 'div',
29709                             cls : 'roo-upload-cropbox-thumb'
29710                         },
29711                         {
29712                             tag : 'div',
29713                             cls : 'roo-upload-cropbox-empty-notify',
29714                             html : this.emptyText
29715                         },
29716                         {
29717                             tag : 'div',
29718                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29719                             html : this.rotateNotify
29720                         }
29721                     ]
29722                 },
29723                 {
29724                     tag : 'div',
29725                     cls : 'roo-upload-cropbox-footer',
29726                     cn : {
29727                         tag : 'div',
29728                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29729                         cn : []
29730                     }
29731                 }
29732             ]
29733         };
29734         
29735         return cfg;
29736     },
29737     
29738     onRender : function(ct, position)
29739     {
29740         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29741         
29742         if (this.buttons.length) {
29743             
29744             Roo.each(this.buttons, function(bb) {
29745                 
29746                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29747                 
29748                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29749                 
29750             }, this);
29751         }
29752         
29753         if(this.loadMask){
29754             this.maskEl = this.el;
29755         }
29756     },
29757     
29758     initEvents : function()
29759     {
29760         this.urlAPI = (window.createObjectURL && window) || 
29761                                 (window.URL && URL.revokeObjectURL && URL) || 
29762                                 (window.webkitURL && webkitURL);
29763                         
29764         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29765         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29766         
29767         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29768         this.selectorEl.hide();
29769         
29770         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29771         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29772         
29773         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29774         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29775         this.thumbEl.hide();
29776         
29777         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29778         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29779         
29780         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29781         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29782         this.errorEl.hide();
29783         
29784         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29785         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29786         this.footerEl.hide();
29787         
29788         this.setThumbBoxSize();
29789         
29790         this.bind();
29791         
29792         this.resize();
29793         
29794         this.fireEvent('initial', this);
29795     },
29796
29797     bind : function()
29798     {
29799         var _this = this;
29800         
29801         window.addEventListener("resize", function() { _this.resize(); } );
29802         
29803         this.bodyEl.on('click', this.beforeSelectFile, this);
29804         
29805         if(Roo.isTouch){
29806             this.bodyEl.on('touchstart', this.onTouchStart, this);
29807             this.bodyEl.on('touchmove', this.onTouchMove, this);
29808             this.bodyEl.on('touchend', this.onTouchEnd, this);
29809         }
29810         
29811         if(!Roo.isTouch){
29812             this.bodyEl.on('mousedown', this.onMouseDown, this);
29813             this.bodyEl.on('mousemove', this.onMouseMove, this);
29814             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29815             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29816             Roo.get(document).on('mouseup', this.onMouseUp, this);
29817         }
29818         
29819         this.selectorEl.on('change', this.onFileSelected, this);
29820     },
29821     
29822     reset : function()
29823     {    
29824         this.scale = 0;
29825         this.baseScale = 1;
29826         this.rotate = 0;
29827         this.baseRotate = 1;
29828         this.dragable = false;
29829         this.pinching = false;
29830         this.mouseX = 0;
29831         this.mouseY = 0;
29832         this.cropData = false;
29833         this.notifyEl.dom.innerHTML = this.emptyText;
29834         
29835         this.selectorEl.dom.value = '';
29836         
29837     },
29838     
29839     resize : function()
29840     {
29841         if(this.fireEvent('resize', this) != false){
29842             this.setThumbBoxPosition();
29843             this.setCanvasPosition();
29844         }
29845     },
29846     
29847     onFooterButtonClick : function(e, el, o, type)
29848     {
29849         switch (type) {
29850             case 'rotate-left' :
29851                 this.onRotateLeft(e);
29852                 break;
29853             case 'rotate-right' :
29854                 this.onRotateRight(e);
29855                 break;
29856             case 'picture' :
29857                 this.beforeSelectFile(e);
29858                 break;
29859             case 'trash' :
29860                 this.trash(e);
29861                 break;
29862             case 'crop' :
29863                 this.crop(e);
29864                 break;
29865             case 'download' :
29866                 this.download(e);
29867                 break;
29868             default :
29869                 break;
29870         }
29871         
29872         this.fireEvent('footerbuttonclick', this, type);
29873     },
29874     
29875     beforeSelectFile : function(e)
29876     {
29877         e.preventDefault();
29878         
29879         if(this.fireEvent('beforeselectfile', this) != false){
29880             this.selectorEl.dom.click();
29881         }
29882     },
29883     
29884     onFileSelected : function(e)
29885     {
29886         e.preventDefault();
29887         
29888         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29889             return;
29890         }
29891         
29892         var file = this.selectorEl.dom.files[0];
29893         
29894         if(this.fireEvent('inspect', this, file) != false){
29895             this.prepare(file);
29896         }
29897         
29898     },
29899     
29900     trash : function(e)
29901     {
29902         this.fireEvent('trash', this);
29903     },
29904     
29905     download : function(e)
29906     {
29907         this.fireEvent('download', this);
29908     },
29909     
29910     loadCanvas : function(src)
29911     {   
29912         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29913             
29914             this.reset();
29915             
29916             this.imageEl = document.createElement('img');
29917             
29918             var _this = this;
29919             
29920             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29921             
29922             this.imageEl.src = src;
29923         }
29924     },
29925     
29926     onLoadCanvas : function()
29927     {   
29928         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29929         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29930         
29931         this.bodyEl.un('click', this.beforeSelectFile, this);
29932         
29933         this.notifyEl.hide();
29934         this.thumbEl.show();
29935         this.footerEl.show();
29936         
29937         this.baseRotateLevel();
29938         
29939         if(this.isDocument){
29940             this.setThumbBoxSize();
29941         }
29942         
29943         this.setThumbBoxPosition();
29944         
29945         this.baseScaleLevel();
29946         
29947         this.draw();
29948         
29949         this.resize();
29950         
29951         this.canvasLoaded = true;
29952         
29953         if(this.loadMask){
29954             this.maskEl.unmask();
29955         }
29956         
29957     },
29958     
29959     setCanvasPosition : function()
29960     {   
29961         if(!this.canvasEl){
29962             return;
29963         }
29964         
29965         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29966         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29967         
29968         this.previewEl.setLeft(pw);
29969         this.previewEl.setTop(ph);
29970         
29971     },
29972     
29973     onMouseDown : function(e)
29974     {   
29975         e.stopEvent();
29976         
29977         this.dragable = true;
29978         this.pinching = false;
29979         
29980         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29981             this.dragable = false;
29982             return;
29983         }
29984         
29985         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29986         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29987         
29988     },
29989     
29990     onMouseMove : function(e)
29991     {   
29992         e.stopEvent();
29993         
29994         if(!this.canvasLoaded){
29995             return;
29996         }
29997         
29998         if (!this.dragable){
29999             return;
30000         }
30001         
30002         var minX = Math.ceil(this.thumbEl.getLeft(true));
30003         var minY = Math.ceil(this.thumbEl.getTop(true));
30004         
30005         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30006         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30007         
30008         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30009         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30010         
30011         x = x - this.mouseX;
30012         y = y - this.mouseY;
30013         
30014         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30015         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30016         
30017         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30018         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30019         
30020         this.previewEl.setLeft(bgX);
30021         this.previewEl.setTop(bgY);
30022         
30023         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30024         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30025     },
30026     
30027     onMouseUp : function(e)
30028     {   
30029         e.stopEvent();
30030         
30031         this.dragable = false;
30032     },
30033     
30034     onMouseWheel : function(e)
30035     {   
30036         e.stopEvent();
30037         
30038         this.startScale = this.scale;
30039         
30040         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30041         
30042         if(!this.zoomable()){
30043             this.scale = this.startScale;
30044             return;
30045         }
30046         
30047         this.draw();
30048         
30049         return;
30050     },
30051     
30052     zoomable : function()
30053     {
30054         var minScale = this.thumbEl.getWidth() / this.minWidth;
30055         
30056         if(this.minWidth < this.minHeight){
30057             minScale = this.thumbEl.getHeight() / this.minHeight;
30058         }
30059         
30060         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30061         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30062         
30063         if(
30064                 this.isDocument &&
30065                 (this.rotate == 0 || this.rotate == 180) && 
30066                 (
30067                     width > this.imageEl.OriginWidth || 
30068                     height > this.imageEl.OriginHeight ||
30069                     (width < this.minWidth && height < this.minHeight)
30070                 )
30071         ){
30072             return false;
30073         }
30074         
30075         if(
30076                 this.isDocument &&
30077                 (this.rotate == 90 || this.rotate == 270) && 
30078                 (
30079                     width > this.imageEl.OriginWidth || 
30080                     height > this.imageEl.OriginHeight ||
30081                     (width < this.minHeight && height < this.minWidth)
30082                 )
30083         ){
30084             return false;
30085         }
30086         
30087         if(
30088                 !this.isDocument &&
30089                 (this.rotate == 0 || this.rotate == 180) && 
30090                 (
30091                     width < this.minWidth || 
30092                     width > this.imageEl.OriginWidth || 
30093                     height < this.minHeight || 
30094                     height > this.imageEl.OriginHeight
30095                 )
30096         ){
30097             return false;
30098         }
30099         
30100         if(
30101                 !this.isDocument &&
30102                 (this.rotate == 90 || this.rotate == 270) && 
30103                 (
30104                     width < this.minHeight || 
30105                     width > this.imageEl.OriginWidth || 
30106                     height < this.minWidth || 
30107                     height > this.imageEl.OriginHeight
30108                 )
30109         ){
30110             return false;
30111         }
30112         
30113         return true;
30114         
30115     },
30116     
30117     onRotateLeft : function(e)
30118     {   
30119         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30120             
30121             var minScale = this.thumbEl.getWidth() / this.minWidth;
30122             
30123             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30124             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30125             
30126             this.startScale = this.scale;
30127             
30128             while (this.getScaleLevel() < minScale){
30129             
30130                 this.scale = this.scale + 1;
30131                 
30132                 if(!this.zoomable()){
30133                     break;
30134                 }
30135                 
30136                 if(
30137                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30138                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30139                 ){
30140                     continue;
30141                 }
30142                 
30143                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30144
30145                 this.draw();
30146                 
30147                 return;
30148             }
30149             
30150             this.scale = this.startScale;
30151             
30152             this.onRotateFail();
30153             
30154             return false;
30155         }
30156         
30157         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30158
30159         if(this.isDocument){
30160             this.setThumbBoxSize();
30161             this.setThumbBoxPosition();
30162             this.setCanvasPosition();
30163         }
30164         
30165         this.draw();
30166         
30167         this.fireEvent('rotate', this, 'left');
30168         
30169     },
30170     
30171     onRotateRight : function(e)
30172     {
30173         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30174             
30175             var minScale = this.thumbEl.getWidth() / this.minWidth;
30176         
30177             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30178             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30179             
30180             this.startScale = this.scale;
30181             
30182             while (this.getScaleLevel() < minScale){
30183             
30184                 this.scale = this.scale + 1;
30185                 
30186                 if(!this.zoomable()){
30187                     break;
30188                 }
30189                 
30190                 if(
30191                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30192                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30193                 ){
30194                     continue;
30195                 }
30196                 
30197                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30198
30199                 this.draw();
30200                 
30201                 return;
30202             }
30203             
30204             this.scale = this.startScale;
30205             
30206             this.onRotateFail();
30207             
30208             return false;
30209         }
30210         
30211         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30212
30213         if(this.isDocument){
30214             this.setThumbBoxSize();
30215             this.setThumbBoxPosition();
30216             this.setCanvasPosition();
30217         }
30218         
30219         this.draw();
30220         
30221         this.fireEvent('rotate', this, 'right');
30222     },
30223     
30224     onRotateFail : function()
30225     {
30226         this.errorEl.show(true);
30227         
30228         var _this = this;
30229         
30230         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30231     },
30232     
30233     draw : function()
30234     {
30235         this.previewEl.dom.innerHTML = '';
30236         
30237         var canvasEl = document.createElement("canvas");
30238         
30239         var contextEl = canvasEl.getContext("2d");
30240         
30241         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30242         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30243         var center = this.imageEl.OriginWidth / 2;
30244         
30245         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30246             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30247             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30248             center = this.imageEl.OriginHeight / 2;
30249         }
30250         
30251         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30252         
30253         contextEl.translate(center, center);
30254         contextEl.rotate(this.rotate * Math.PI / 180);
30255
30256         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30257         
30258         this.canvasEl = document.createElement("canvas");
30259         
30260         this.contextEl = this.canvasEl.getContext("2d");
30261         
30262         switch (this.rotate) {
30263             case 0 :
30264                 
30265                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30266                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30267                 
30268                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30269                 
30270                 break;
30271             case 90 : 
30272                 
30273                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30274                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30275                 
30276                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30277                     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);
30278                     break;
30279                 }
30280                 
30281                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30282                 
30283                 break;
30284             case 180 :
30285                 
30286                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30287                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30288                 
30289                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30290                     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);
30291                     break;
30292                 }
30293                 
30294                 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);
30295                 
30296                 break;
30297             case 270 :
30298                 
30299                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30300                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30301         
30302                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30303                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30304                     break;
30305                 }
30306                 
30307                 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);
30308                 
30309                 break;
30310             default : 
30311                 break;
30312         }
30313         
30314         this.previewEl.appendChild(this.canvasEl);
30315         
30316         this.setCanvasPosition();
30317     },
30318     
30319     crop : function()
30320     {
30321         if(!this.canvasLoaded){
30322             return;
30323         }
30324         
30325         var imageCanvas = document.createElement("canvas");
30326         
30327         var imageContext = imageCanvas.getContext("2d");
30328         
30329         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30330         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30331         
30332         var center = imageCanvas.width / 2;
30333         
30334         imageContext.translate(center, center);
30335         
30336         imageContext.rotate(this.rotate * Math.PI / 180);
30337         
30338         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30339         
30340         var canvas = document.createElement("canvas");
30341         
30342         var context = canvas.getContext("2d");
30343                 
30344         canvas.width = this.minWidth;
30345         canvas.height = this.minHeight;
30346
30347         switch (this.rotate) {
30348             case 0 :
30349                 
30350                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30351                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30352                 
30353                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30354                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30355                 
30356                 var targetWidth = this.minWidth - 2 * x;
30357                 var targetHeight = this.minHeight - 2 * y;
30358                 
30359                 var scale = 1;
30360                 
30361                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30362                     scale = targetWidth / width;
30363                 }
30364                 
30365                 if(x > 0 && y == 0){
30366                     scale = targetHeight / height;
30367                 }
30368                 
30369                 if(x > 0 && y > 0){
30370                     scale = targetWidth / width;
30371                     
30372                     if(width < height){
30373                         scale = targetHeight / height;
30374                     }
30375                 }
30376                 
30377                 context.scale(scale, scale);
30378                 
30379                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30380                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30381
30382                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30383                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30384
30385                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30386                 
30387                 break;
30388             case 90 : 
30389                 
30390                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30391                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30392                 
30393                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30394                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30395                 
30396                 var targetWidth = this.minWidth - 2 * x;
30397                 var targetHeight = this.minHeight - 2 * y;
30398                 
30399                 var scale = 1;
30400                 
30401                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30402                     scale = targetWidth / width;
30403                 }
30404                 
30405                 if(x > 0 && y == 0){
30406                     scale = targetHeight / height;
30407                 }
30408                 
30409                 if(x > 0 && y > 0){
30410                     scale = targetWidth / width;
30411                     
30412                     if(width < height){
30413                         scale = targetHeight / height;
30414                     }
30415                 }
30416                 
30417                 context.scale(scale, scale);
30418                 
30419                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30420                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30421
30422                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30423                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30424                 
30425                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30426                 
30427                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30428                 
30429                 break;
30430             case 180 :
30431                 
30432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30434                 
30435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30437                 
30438                 var targetWidth = this.minWidth - 2 * x;
30439                 var targetHeight = this.minHeight - 2 * y;
30440                 
30441                 var scale = 1;
30442                 
30443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30444                     scale = targetWidth / width;
30445                 }
30446                 
30447                 if(x > 0 && y == 0){
30448                     scale = targetHeight / height;
30449                 }
30450                 
30451                 if(x > 0 && y > 0){
30452                     scale = targetWidth / width;
30453                     
30454                     if(width < height){
30455                         scale = targetHeight / height;
30456                     }
30457                 }
30458                 
30459                 context.scale(scale, scale);
30460                 
30461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30463
30464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30466
30467                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30468                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30469                 
30470                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30471                 
30472                 break;
30473             case 270 :
30474                 
30475                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30476                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30477                 
30478                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30479                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30480                 
30481                 var targetWidth = this.minWidth - 2 * x;
30482                 var targetHeight = this.minHeight - 2 * y;
30483                 
30484                 var scale = 1;
30485                 
30486                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30487                     scale = targetWidth / width;
30488                 }
30489                 
30490                 if(x > 0 && y == 0){
30491                     scale = targetHeight / height;
30492                 }
30493                 
30494                 if(x > 0 && y > 0){
30495                     scale = targetWidth / width;
30496                     
30497                     if(width < height){
30498                         scale = targetHeight / height;
30499                     }
30500                 }
30501                 
30502                 context.scale(scale, scale);
30503                 
30504                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30505                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30506
30507                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30508                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30509                 
30510                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30511                 
30512                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30513                 
30514                 break;
30515             default : 
30516                 break;
30517         }
30518         
30519         this.cropData = canvas.toDataURL(this.cropType);
30520         
30521         if(this.fireEvent('crop', this, this.cropData) !== false){
30522             this.process(this.file, this.cropData);
30523         }
30524         
30525         return;
30526         
30527     },
30528     
30529     setThumbBoxSize : function()
30530     {
30531         var width, height;
30532         
30533         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30534             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30535             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30536             
30537             this.minWidth = width;
30538             this.minHeight = height;
30539             
30540             if(this.rotate == 90 || this.rotate == 270){
30541                 this.minWidth = height;
30542                 this.minHeight = width;
30543             }
30544         }
30545         
30546         height = 300;
30547         width = Math.ceil(this.minWidth * height / this.minHeight);
30548         
30549         if(this.minWidth > this.minHeight){
30550             width = 300;
30551             height = Math.ceil(this.minHeight * width / this.minWidth);
30552         }
30553         
30554         this.thumbEl.setStyle({
30555             width : width + 'px',
30556             height : height + 'px'
30557         });
30558
30559         return;
30560             
30561     },
30562     
30563     setThumbBoxPosition : function()
30564     {
30565         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30566         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30567         
30568         this.thumbEl.setLeft(x);
30569         this.thumbEl.setTop(y);
30570         
30571     },
30572     
30573     baseRotateLevel : function()
30574     {
30575         this.baseRotate = 1;
30576         
30577         if(
30578                 typeof(this.exif) != 'undefined' &&
30579                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30580                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30581         ){
30582             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30583         }
30584         
30585         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30586         
30587     },
30588     
30589     baseScaleLevel : function()
30590     {
30591         var width, height;
30592         
30593         if(this.isDocument){
30594             
30595             if(this.baseRotate == 6 || this.baseRotate == 8){
30596             
30597                 height = this.thumbEl.getHeight();
30598                 this.baseScale = height / this.imageEl.OriginWidth;
30599
30600                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30601                     width = this.thumbEl.getWidth();
30602                     this.baseScale = width / this.imageEl.OriginHeight;
30603                 }
30604
30605                 return;
30606             }
30607
30608             height = this.thumbEl.getHeight();
30609             this.baseScale = height / this.imageEl.OriginHeight;
30610
30611             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30612                 width = this.thumbEl.getWidth();
30613                 this.baseScale = width / this.imageEl.OriginWidth;
30614             }
30615
30616             return;
30617         }
30618         
30619         if(this.baseRotate == 6 || this.baseRotate == 8){
30620             
30621             width = this.thumbEl.getHeight();
30622             this.baseScale = width / this.imageEl.OriginHeight;
30623             
30624             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30625                 height = this.thumbEl.getWidth();
30626                 this.baseScale = height / this.imageEl.OriginHeight;
30627             }
30628             
30629             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30630                 height = this.thumbEl.getWidth();
30631                 this.baseScale = height / this.imageEl.OriginHeight;
30632                 
30633                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30634                     width = this.thumbEl.getHeight();
30635                     this.baseScale = width / this.imageEl.OriginWidth;
30636                 }
30637             }
30638             
30639             return;
30640         }
30641         
30642         width = this.thumbEl.getWidth();
30643         this.baseScale = width / this.imageEl.OriginWidth;
30644         
30645         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30646             height = this.thumbEl.getHeight();
30647             this.baseScale = height / this.imageEl.OriginHeight;
30648         }
30649         
30650         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30651             
30652             height = this.thumbEl.getHeight();
30653             this.baseScale = height / this.imageEl.OriginHeight;
30654             
30655             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30656                 width = this.thumbEl.getWidth();
30657                 this.baseScale = width / this.imageEl.OriginWidth;
30658             }
30659             
30660         }
30661         
30662         return;
30663     },
30664     
30665     getScaleLevel : function()
30666     {
30667         return this.baseScale * Math.pow(1.1, this.scale);
30668     },
30669     
30670     onTouchStart : function(e)
30671     {
30672         if(!this.canvasLoaded){
30673             this.beforeSelectFile(e);
30674             return;
30675         }
30676         
30677         var touches = e.browserEvent.touches;
30678         
30679         if(!touches){
30680             return;
30681         }
30682         
30683         if(touches.length == 1){
30684             this.onMouseDown(e);
30685             return;
30686         }
30687         
30688         if(touches.length != 2){
30689             return;
30690         }
30691         
30692         var coords = [];
30693         
30694         for(var i = 0, finger; finger = touches[i]; i++){
30695             coords.push(finger.pageX, finger.pageY);
30696         }
30697         
30698         var x = Math.pow(coords[0] - coords[2], 2);
30699         var y = Math.pow(coords[1] - coords[3], 2);
30700         
30701         this.startDistance = Math.sqrt(x + y);
30702         
30703         this.startScale = this.scale;
30704         
30705         this.pinching = true;
30706         this.dragable = false;
30707         
30708     },
30709     
30710     onTouchMove : function(e)
30711     {
30712         if(!this.pinching && !this.dragable){
30713             return;
30714         }
30715         
30716         var touches = e.browserEvent.touches;
30717         
30718         if(!touches){
30719             return;
30720         }
30721         
30722         if(this.dragable){
30723             this.onMouseMove(e);
30724             return;
30725         }
30726         
30727         var coords = [];
30728         
30729         for(var i = 0, finger; finger = touches[i]; i++){
30730             coords.push(finger.pageX, finger.pageY);
30731         }
30732         
30733         var x = Math.pow(coords[0] - coords[2], 2);
30734         var y = Math.pow(coords[1] - coords[3], 2);
30735         
30736         this.endDistance = Math.sqrt(x + y);
30737         
30738         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30739         
30740         if(!this.zoomable()){
30741             this.scale = this.startScale;
30742             return;
30743         }
30744         
30745         this.draw();
30746         
30747     },
30748     
30749     onTouchEnd : function(e)
30750     {
30751         this.pinching = false;
30752         this.dragable = false;
30753         
30754     },
30755     
30756     process : function(file, crop)
30757     {
30758         if(this.loadMask){
30759             this.maskEl.mask(this.loadingText);
30760         }
30761         
30762         this.xhr = new XMLHttpRequest();
30763         
30764         file.xhr = this.xhr;
30765
30766         this.xhr.open(this.method, this.url, true);
30767         
30768         var headers = {
30769             "Accept": "application/json",
30770             "Cache-Control": "no-cache",
30771             "X-Requested-With": "XMLHttpRequest"
30772         };
30773         
30774         for (var headerName in headers) {
30775             var headerValue = headers[headerName];
30776             if (headerValue) {
30777                 this.xhr.setRequestHeader(headerName, headerValue);
30778             }
30779         }
30780         
30781         var _this = this;
30782         
30783         this.xhr.onload = function()
30784         {
30785             _this.xhrOnLoad(_this.xhr);
30786         }
30787         
30788         this.xhr.onerror = function()
30789         {
30790             _this.xhrOnError(_this.xhr);
30791         }
30792         
30793         var formData = new FormData();
30794
30795         formData.append('returnHTML', 'NO');
30796         
30797         if(crop){
30798             formData.append('crop', crop);
30799         }
30800         
30801         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30802             formData.append(this.paramName, file, file.name);
30803         }
30804         
30805         if(typeof(file.filename) != 'undefined'){
30806             formData.append('filename', file.filename);
30807         }
30808         
30809         if(typeof(file.mimetype) != 'undefined'){
30810             formData.append('mimetype', file.mimetype);
30811         }
30812         
30813         if(this.fireEvent('arrange', this, formData) != false){
30814             this.xhr.send(formData);
30815         };
30816     },
30817     
30818     xhrOnLoad : function(xhr)
30819     {
30820         if(this.loadMask){
30821             this.maskEl.unmask();
30822         }
30823         
30824         if (xhr.readyState !== 4) {
30825             this.fireEvent('exception', this, xhr);
30826             return;
30827         }
30828
30829         var response = Roo.decode(xhr.responseText);
30830         
30831         if(!response.success){
30832             this.fireEvent('exception', this, xhr);
30833             return;
30834         }
30835         
30836         var response = Roo.decode(xhr.responseText);
30837         
30838         this.fireEvent('upload', this, response);
30839         
30840     },
30841     
30842     xhrOnError : function()
30843     {
30844         if(this.loadMask){
30845             this.maskEl.unmask();
30846         }
30847         
30848         Roo.log('xhr on error');
30849         
30850         var response = Roo.decode(xhr.responseText);
30851           
30852         Roo.log(response);
30853         
30854     },
30855     
30856     prepare : function(file)
30857     {   
30858         if(this.loadMask){
30859             this.maskEl.mask(this.loadingText);
30860         }
30861         
30862         this.file = false;
30863         this.exif = {};
30864         
30865         if(typeof(file) === 'string'){
30866             this.loadCanvas(file);
30867             return;
30868         }
30869         
30870         if(!file || !this.urlAPI){
30871             return;
30872         }
30873         
30874         this.file = file;
30875         this.cropType = file.type;
30876         
30877         var _this = this;
30878         
30879         if(this.fireEvent('prepare', this, this.file) != false){
30880             
30881             var reader = new FileReader();
30882             
30883             reader.onload = function (e) {
30884                 if (e.target.error) {
30885                     Roo.log(e.target.error);
30886                     return;
30887                 }
30888                 
30889                 var buffer = e.target.result,
30890                     dataView = new DataView(buffer),
30891                     offset = 2,
30892                     maxOffset = dataView.byteLength - 4,
30893                     markerBytes,
30894                     markerLength;
30895                 
30896                 if (dataView.getUint16(0) === 0xffd8) {
30897                     while (offset < maxOffset) {
30898                         markerBytes = dataView.getUint16(offset);
30899                         
30900                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30901                             markerLength = dataView.getUint16(offset + 2) + 2;
30902                             if (offset + markerLength > dataView.byteLength) {
30903                                 Roo.log('Invalid meta data: Invalid segment size.');
30904                                 break;
30905                             }
30906                             
30907                             if(markerBytes == 0xffe1){
30908                                 _this.parseExifData(
30909                                     dataView,
30910                                     offset,
30911                                     markerLength
30912                                 );
30913                             }
30914                             
30915                             offset += markerLength;
30916                             
30917                             continue;
30918                         }
30919                         
30920                         break;
30921                     }
30922                     
30923                 }
30924                 
30925                 var url = _this.urlAPI.createObjectURL(_this.file);
30926                 
30927                 _this.loadCanvas(url);
30928                 
30929                 return;
30930             }
30931             
30932             reader.readAsArrayBuffer(this.file);
30933             
30934         }
30935         
30936     },
30937     
30938     parseExifData : function(dataView, offset, length)
30939     {
30940         var tiffOffset = offset + 10,
30941             littleEndian,
30942             dirOffset;
30943     
30944         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30945             // No Exif data, might be XMP data instead
30946             return;
30947         }
30948         
30949         // Check for the ASCII code for "Exif" (0x45786966):
30950         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30951             // No Exif data, might be XMP data instead
30952             return;
30953         }
30954         if (tiffOffset + 8 > dataView.byteLength) {
30955             Roo.log('Invalid Exif data: Invalid segment size.');
30956             return;
30957         }
30958         // Check for the two null bytes:
30959         if (dataView.getUint16(offset + 8) !== 0x0000) {
30960             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30961             return;
30962         }
30963         // Check the byte alignment:
30964         switch (dataView.getUint16(tiffOffset)) {
30965         case 0x4949:
30966             littleEndian = true;
30967             break;
30968         case 0x4D4D:
30969             littleEndian = false;
30970             break;
30971         default:
30972             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30973             return;
30974         }
30975         // Check for the TIFF tag marker (0x002A):
30976         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30977             Roo.log('Invalid Exif data: Missing TIFF marker.');
30978             return;
30979         }
30980         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30981         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30982         
30983         this.parseExifTags(
30984             dataView,
30985             tiffOffset,
30986             tiffOffset + dirOffset,
30987             littleEndian
30988         );
30989     },
30990     
30991     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30992     {
30993         var tagsNumber,
30994             dirEndOffset,
30995             i;
30996         if (dirOffset + 6 > dataView.byteLength) {
30997             Roo.log('Invalid Exif data: Invalid directory offset.');
30998             return;
30999         }
31000         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31001         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31002         if (dirEndOffset + 4 > dataView.byteLength) {
31003             Roo.log('Invalid Exif data: Invalid directory size.');
31004             return;
31005         }
31006         for (i = 0; i < tagsNumber; i += 1) {
31007             this.parseExifTag(
31008                 dataView,
31009                 tiffOffset,
31010                 dirOffset + 2 + 12 * i, // tag offset
31011                 littleEndian
31012             );
31013         }
31014         // Return the offset to the next directory:
31015         return dataView.getUint32(dirEndOffset, littleEndian);
31016     },
31017     
31018     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31019     {
31020         var tag = dataView.getUint16(offset, littleEndian);
31021         
31022         this.exif[tag] = this.getExifValue(
31023             dataView,
31024             tiffOffset,
31025             offset,
31026             dataView.getUint16(offset + 2, littleEndian), // tag type
31027             dataView.getUint32(offset + 4, littleEndian), // tag length
31028             littleEndian
31029         );
31030     },
31031     
31032     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31033     {
31034         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31035             tagSize,
31036             dataOffset,
31037             values,
31038             i,
31039             str,
31040             c;
31041     
31042         if (!tagType) {
31043             Roo.log('Invalid Exif data: Invalid tag type.');
31044             return;
31045         }
31046         
31047         tagSize = tagType.size * length;
31048         // Determine if the value is contained in the dataOffset bytes,
31049         // or if the value at the dataOffset is a pointer to the actual data:
31050         dataOffset = tagSize > 4 ?
31051                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31052         if (dataOffset + tagSize > dataView.byteLength) {
31053             Roo.log('Invalid Exif data: Invalid data offset.');
31054             return;
31055         }
31056         if (length === 1) {
31057             return tagType.getValue(dataView, dataOffset, littleEndian);
31058         }
31059         values = [];
31060         for (i = 0; i < length; i += 1) {
31061             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31062         }
31063         
31064         if (tagType.ascii) {
31065             str = '';
31066             // Concatenate the chars:
31067             for (i = 0; i < values.length; i += 1) {
31068                 c = values[i];
31069                 // Ignore the terminating NULL byte(s):
31070                 if (c === '\u0000') {
31071                     break;
31072                 }
31073                 str += c;
31074             }
31075             return str;
31076         }
31077         return values;
31078     }
31079     
31080 });
31081
31082 Roo.apply(Roo.bootstrap.UploadCropbox, {
31083     tags : {
31084         'Orientation': 0x0112
31085     },
31086     
31087     Orientation: {
31088             1: 0, //'top-left',
31089 //            2: 'top-right',
31090             3: 180, //'bottom-right',
31091 //            4: 'bottom-left',
31092 //            5: 'left-top',
31093             6: 90, //'right-top',
31094 //            7: 'right-bottom',
31095             8: 270 //'left-bottom'
31096     },
31097     
31098     exifTagTypes : {
31099         // byte, 8-bit unsigned int:
31100         1: {
31101             getValue: function (dataView, dataOffset) {
31102                 return dataView.getUint8(dataOffset);
31103             },
31104             size: 1
31105         },
31106         // ascii, 8-bit byte:
31107         2: {
31108             getValue: function (dataView, dataOffset) {
31109                 return String.fromCharCode(dataView.getUint8(dataOffset));
31110             },
31111             size: 1,
31112             ascii: true
31113         },
31114         // short, 16 bit int:
31115         3: {
31116             getValue: function (dataView, dataOffset, littleEndian) {
31117                 return dataView.getUint16(dataOffset, littleEndian);
31118             },
31119             size: 2
31120         },
31121         // long, 32 bit int:
31122         4: {
31123             getValue: function (dataView, dataOffset, littleEndian) {
31124                 return dataView.getUint32(dataOffset, littleEndian);
31125             },
31126             size: 4
31127         },
31128         // rational = two long values, first is numerator, second is denominator:
31129         5: {
31130             getValue: function (dataView, dataOffset, littleEndian) {
31131                 return dataView.getUint32(dataOffset, littleEndian) /
31132                     dataView.getUint32(dataOffset + 4, littleEndian);
31133             },
31134             size: 8
31135         },
31136         // slong, 32 bit signed int:
31137         9: {
31138             getValue: function (dataView, dataOffset, littleEndian) {
31139                 return dataView.getInt32(dataOffset, littleEndian);
31140             },
31141             size: 4
31142         },
31143         // srational, two slongs, first is numerator, second is denominator:
31144         10: {
31145             getValue: function (dataView, dataOffset, littleEndian) {
31146                 return dataView.getInt32(dataOffset, littleEndian) /
31147                     dataView.getInt32(dataOffset + 4, littleEndian);
31148             },
31149             size: 8
31150         }
31151     },
31152     
31153     footer : {
31154         STANDARD : [
31155             {
31156                 tag : 'div',
31157                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31158                 action : 'rotate-left',
31159                 cn : [
31160                     {
31161                         tag : 'button',
31162                         cls : 'btn btn-default',
31163                         html : '<i class="fa fa-undo"></i>'
31164                     }
31165                 ]
31166             },
31167             {
31168                 tag : 'div',
31169                 cls : 'btn-group roo-upload-cropbox-picture',
31170                 action : 'picture',
31171                 cn : [
31172                     {
31173                         tag : 'button',
31174                         cls : 'btn btn-default',
31175                         html : '<i class="fa fa-picture-o"></i>'
31176                     }
31177                 ]
31178             },
31179             {
31180                 tag : 'div',
31181                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31182                 action : 'rotate-right',
31183                 cn : [
31184                     {
31185                         tag : 'button',
31186                         cls : 'btn btn-default',
31187                         html : '<i class="fa fa-repeat"></i>'
31188                     }
31189                 ]
31190             }
31191         ],
31192         DOCUMENT : [
31193             {
31194                 tag : 'div',
31195                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31196                 action : 'rotate-left',
31197                 cn : [
31198                     {
31199                         tag : 'button',
31200                         cls : 'btn btn-default',
31201                         html : '<i class="fa fa-undo"></i>'
31202                     }
31203                 ]
31204             },
31205             {
31206                 tag : 'div',
31207                 cls : 'btn-group roo-upload-cropbox-download',
31208                 action : 'download',
31209                 cn : [
31210                     {
31211                         tag : 'button',
31212                         cls : 'btn btn-default',
31213                         html : '<i class="fa fa-download"></i>'
31214                     }
31215                 ]
31216             },
31217             {
31218                 tag : 'div',
31219                 cls : 'btn-group roo-upload-cropbox-crop',
31220                 action : 'crop',
31221                 cn : [
31222                     {
31223                         tag : 'button',
31224                         cls : 'btn btn-default',
31225                         html : '<i class="fa fa-crop"></i>'
31226                     }
31227                 ]
31228             },
31229             {
31230                 tag : 'div',
31231                 cls : 'btn-group roo-upload-cropbox-trash',
31232                 action : 'trash',
31233                 cn : [
31234                     {
31235                         tag : 'button',
31236                         cls : 'btn btn-default',
31237                         html : '<i class="fa fa-trash"></i>'
31238                     }
31239                 ]
31240             },
31241             {
31242                 tag : 'div',
31243                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31244                 action : 'rotate-right',
31245                 cn : [
31246                     {
31247                         tag : 'button',
31248                         cls : 'btn btn-default',
31249                         html : '<i class="fa fa-repeat"></i>'
31250                     }
31251                 ]
31252             }
31253         ],
31254         ROTATOR : [
31255             {
31256                 tag : 'div',
31257                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31258                 action : 'rotate-left',
31259                 cn : [
31260                     {
31261                         tag : 'button',
31262                         cls : 'btn btn-default',
31263                         html : '<i class="fa fa-undo"></i>'
31264                     }
31265                 ]
31266             },
31267             {
31268                 tag : 'div',
31269                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31270                 action : 'rotate-right',
31271                 cn : [
31272                     {
31273                         tag : 'button',
31274                         cls : 'btn btn-default',
31275                         html : '<i class="fa fa-repeat"></i>'
31276                     }
31277                 ]
31278             }
31279         ]
31280     }
31281 });
31282
31283 /*
31284 * Licence: LGPL
31285 */
31286
31287 /**
31288  * @class Roo.bootstrap.DocumentManager
31289  * @extends Roo.bootstrap.Component
31290  * Bootstrap DocumentManager class
31291  * @cfg {String} paramName default 'imageUpload'
31292  * @cfg {String} toolTipName default 'filename'
31293  * @cfg {String} method default POST
31294  * @cfg {String} url action url
31295  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31296  * @cfg {Boolean} multiple multiple upload default true
31297  * @cfg {Number} thumbSize default 300
31298  * @cfg {String} fieldLabel
31299  * @cfg {Number} labelWidth default 4
31300  * @cfg {String} labelAlign (left|top) default left
31301  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31302 * @cfg {Number} labellg set the width of label (1-12)
31303  * @cfg {Number} labelmd set the width of label (1-12)
31304  * @cfg {Number} labelsm set the width of label (1-12)
31305  * @cfg {Number} labelxs set the width of label (1-12)
31306  * 
31307  * @constructor
31308  * Create a new DocumentManager
31309  * @param {Object} config The config object
31310  */
31311
31312 Roo.bootstrap.DocumentManager = function(config){
31313     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31314     
31315     this.files = [];
31316     this.delegates = [];
31317     
31318     this.addEvents({
31319         /**
31320          * @event initial
31321          * Fire when initial the DocumentManager
31322          * @param {Roo.bootstrap.DocumentManager} this
31323          */
31324         "initial" : true,
31325         /**
31326          * @event inspect
31327          * inspect selected file
31328          * @param {Roo.bootstrap.DocumentManager} this
31329          * @param {File} file
31330          */
31331         "inspect" : true,
31332         /**
31333          * @event exception
31334          * Fire when xhr load exception
31335          * @param {Roo.bootstrap.DocumentManager} this
31336          * @param {XMLHttpRequest} xhr
31337          */
31338         "exception" : true,
31339         /**
31340          * @event afterupload
31341          * Fire when xhr load exception
31342          * @param {Roo.bootstrap.DocumentManager} this
31343          * @param {XMLHttpRequest} xhr
31344          */
31345         "afterupload" : true,
31346         /**
31347          * @event prepare
31348          * prepare the form data
31349          * @param {Roo.bootstrap.DocumentManager} this
31350          * @param {Object} formData
31351          */
31352         "prepare" : true,
31353         /**
31354          * @event remove
31355          * Fire when remove the file
31356          * @param {Roo.bootstrap.DocumentManager} this
31357          * @param {Object} file
31358          */
31359         "remove" : true,
31360         /**
31361          * @event refresh
31362          * Fire after refresh the file
31363          * @param {Roo.bootstrap.DocumentManager} this
31364          */
31365         "refresh" : true,
31366         /**
31367          * @event click
31368          * Fire after click the image
31369          * @param {Roo.bootstrap.DocumentManager} this
31370          * @param {Object} file
31371          */
31372         "click" : true,
31373         /**
31374          * @event edit
31375          * Fire when upload a image and editable set to true
31376          * @param {Roo.bootstrap.DocumentManager} this
31377          * @param {Object} file
31378          */
31379         "edit" : true,
31380         /**
31381          * @event beforeselectfile
31382          * Fire before select file
31383          * @param {Roo.bootstrap.DocumentManager} this
31384          */
31385         "beforeselectfile" : true,
31386         /**
31387          * @event process
31388          * Fire before process file
31389          * @param {Roo.bootstrap.DocumentManager} this
31390          * @param {Object} file
31391          */
31392         "process" : true,
31393         /**
31394          * @event previewrendered
31395          * Fire when preview rendered
31396          * @param {Roo.bootstrap.DocumentManager} this
31397          * @param {Object} file
31398          */
31399         "previewrendered" : true,
31400         /**
31401          */
31402         "previewResize" : true
31403         
31404     });
31405 };
31406
31407 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31408     
31409     boxes : 0,
31410     inputName : '',
31411     thumbSize : 300,
31412     multiple : true,
31413     files : false,
31414     method : 'POST',
31415     url : '',
31416     paramName : 'imageUpload',
31417     toolTipName : 'filename',
31418     fieldLabel : '',
31419     labelWidth : 4,
31420     labelAlign : 'left',
31421     editable : true,
31422     delegates : false,
31423     xhr : false, 
31424     
31425     labellg : 0,
31426     labelmd : 0,
31427     labelsm : 0,
31428     labelxs : 0,
31429     
31430     getAutoCreate : function()
31431     {   
31432         var managerWidget = {
31433             tag : 'div',
31434             cls : 'roo-document-manager',
31435             cn : [
31436                 {
31437                     tag : 'input',
31438                     cls : 'roo-document-manager-selector',
31439                     type : 'file'
31440                 },
31441                 {
31442                     tag : 'div',
31443                     cls : 'roo-document-manager-uploader',
31444                     cn : [
31445                         {
31446                             tag : 'div',
31447                             cls : 'roo-document-manager-upload-btn',
31448                             html : '<i class="fa fa-plus"></i>'
31449                         }
31450                     ]
31451                     
31452                 }
31453             ]
31454         };
31455         
31456         var content = [
31457             {
31458                 tag : 'div',
31459                 cls : 'column col-md-12',
31460                 cn : managerWidget
31461             }
31462         ];
31463         
31464         if(this.fieldLabel.length){
31465             
31466             content = [
31467                 {
31468                     tag : 'div',
31469                     cls : 'column col-md-12',
31470                     html : this.fieldLabel
31471                 },
31472                 {
31473                     tag : 'div',
31474                     cls : 'column col-md-12',
31475                     cn : managerWidget
31476                 }
31477             ];
31478
31479             if(this.labelAlign == 'left'){
31480                 content = [
31481                     {
31482                         tag : 'div',
31483                         cls : 'column',
31484                         html : this.fieldLabel
31485                     },
31486                     {
31487                         tag : 'div',
31488                         cls : 'column',
31489                         cn : managerWidget
31490                     }
31491                 ];
31492                 
31493                 if(this.labelWidth > 12){
31494                     content[0].style = "width: " + this.labelWidth + 'px';
31495                 }
31496
31497                 if(this.labelWidth < 13 && this.labelmd == 0){
31498                     this.labelmd = this.labelWidth;
31499                 }
31500
31501                 if(this.labellg > 0){
31502                     content[0].cls += ' col-lg-' + this.labellg;
31503                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31504                 }
31505
31506                 if(this.labelmd > 0){
31507                     content[0].cls += ' col-md-' + this.labelmd;
31508                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31509                 }
31510
31511                 if(this.labelsm > 0){
31512                     content[0].cls += ' col-sm-' + this.labelsm;
31513                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31514                 }
31515
31516                 if(this.labelxs > 0){
31517                     content[0].cls += ' col-xs-' + this.labelxs;
31518                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31519                 }
31520                 
31521             }
31522         }
31523         
31524         var cfg = {
31525             tag : 'div',
31526             cls : 'row clearfix',
31527             cn : content
31528         };
31529         
31530         return cfg;
31531         
31532     },
31533     
31534     initEvents : function()
31535     {
31536         this.managerEl = this.el.select('.roo-document-manager', true).first();
31537         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31538         
31539         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31540         this.selectorEl.hide();
31541         
31542         if(this.multiple){
31543             this.selectorEl.attr('multiple', 'multiple');
31544         }
31545         
31546         this.selectorEl.on('change', this.onFileSelected, this);
31547         
31548         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31549         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31550         
31551         this.uploader.on('click', this.onUploaderClick, this);
31552         
31553         this.renderProgressDialog();
31554         
31555         var _this = this;
31556         
31557         window.addEventListener("resize", function() { _this.refresh(); } );
31558         
31559         this.fireEvent('initial', this);
31560     },
31561     
31562     renderProgressDialog : function()
31563     {
31564         var _this = this;
31565         
31566         this.progressDialog = new Roo.bootstrap.Modal({
31567             cls : 'roo-document-manager-progress-dialog',
31568             allow_close : false,
31569             animate : false,
31570             title : '',
31571             buttons : [
31572                 {
31573                     name  :'cancel',
31574                     weight : 'danger',
31575                     html : 'Cancel'
31576                 }
31577             ], 
31578             listeners : { 
31579                 btnclick : function() {
31580                     _this.uploadCancel();
31581                     this.hide();
31582                 }
31583             }
31584         });
31585          
31586         this.progressDialog.render(Roo.get(document.body));
31587          
31588         this.progress = new Roo.bootstrap.Progress({
31589             cls : 'roo-document-manager-progress',
31590             active : true,
31591             striped : true
31592         });
31593         
31594         this.progress.render(this.progressDialog.getChildContainer());
31595         
31596         this.progressBar = new Roo.bootstrap.ProgressBar({
31597             cls : 'roo-document-manager-progress-bar',
31598             aria_valuenow : 0,
31599             aria_valuemin : 0,
31600             aria_valuemax : 12,
31601             panel : 'success'
31602         });
31603         
31604         this.progressBar.render(this.progress.getChildContainer());
31605     },
31606     
31607     onUploaderClick : function(e)
31608     {
31609         e.preventDefault();
31610      
31611         if(this.fireEvent('beforeselectfile', this) != false){
31612             this.selectorEl.dom.click();
31613         }
31614         
31615     },
31616     
31617     onFileSelected : function(e)
31618     {
31619         e.preventDefault();
31620         
31621         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31622             return;
31623         }
31624         
31625         Roo.each(this.selectorEl.dom.files, function(file){
31626             if(this.fireEvent('inspect', this, file) != false){
31627                 this.files.push(file);
31628             }
31629         }, this);
31630         
31631         this.queue();
31632         
31633     },
31634     
31635     queue : function()
31636     {
31637         this.selectorEl.dom.value = '';
31638         
31639         if(!this.files || !this.files.length){
31640             return;
31641         }
31642         
31643         if(this.boxes > 0 && this.files.length > this.boxes){
31644             this.files = this.files.slice(0, this.boxes);
31645         }
31646         
31647         this.uploader.show();
31648         
31649         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31650             this.uploader.hide();
31651         }
31652         
31653         var _this = this;
31654         
31655         var files = [];
31656         
31657         var docs = [];
31658         
31659         Roo.each(this.files, function(file){
31660             
31661             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31662                 var f = this.renderPreview(file);
31663                 files.push(f);
31664                 return;
31665             }
31666             
31667             if(file.type.indexOf('image') != -1){
31668                 this.delegates.push(
31669                     (function(){
31670                         _this.process(file);
31671                     }).createDelegate(this)
31672                 );
31673         
31674                 return;
31675             }
31676             
31677             docs.push(
31678                 (function(){
31679                     _this.process(file);
31680                 }).createDelegate(this)
31681             );
31682             
31683         }, this);
31684         
31685         this.files = files;
31686         
31687         this.delegates = this.delegates.concat(docs);
31688         
31689         if(!this.delegates.length){
31690             this.refresh();
31691             return;
31692         }
31693         
31694         this.progressBar.aria_valuemax = this.delegates.length;
31695         
31696         this.arrange();
31697         
31698         return;
31699     },
31700     
31701     arrange : function()
31702     {
31703         if(!this.delegates.length){
31704             this.progressDialog.hide();
31705             this.refresh();
31706             return;
31707         }
31708         
31709         var delegate = this.delegates.shift();
31710         
31711         this.progressDialog.show();
31712         
31713         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31714         
31715         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31716         
31717         delegate();
31718     },
31719     
31720     refresh : function()
31721     {
31722         this.uploader.show();
31723         
31724         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31725             this.uploader.hide();
31726         }
31727         
31728         Roo.isTouch ? this.closable(false) : this.closable(true);
31729         
31730         this.fireEvent('refresh', this);
31731     },
31732     
31733     onRemove : function(e, el, o)
31734     {
31735         e.preventDefault();
31736         
31737         this.fireEvent('remove', this, o);
31738         
31739     },
31740     
31741     remove : function(o)
31742     {
31743         var files = [];
31744         
31745         Roo.each(this.files, function(file){
31746             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31747                 files.push(file);
31748                 return;
31749             }
31750
31751             o.target.remove();
31752
31753         }, this);
31754         
31755         this.files = files;
31756         
31757         this.refresh();
31758     },
31759     
31760     clear : function()
31761     {
31762         Roo.each(this.files, function(file){
31763             if(!file.target){
31764                 return;
31765             }
31766             
31767             file.target.remove();
31768
31769         }, this);
31770         
31771         this.files = [];
31772         
31773         this.refresh();
31774     },
31775     
31776     onClick : function(e, el, o)
31777     {
31778         e.preventDefault();
31779         
31780         this.fireEvent('click', this, o);
31781         
31782     },
31783     
31784     closable : function(closable)
31785     {
31786         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31787             
31788             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31789             
31790             if(closable){
31791                 el.show();
31792                 return;
31793             }
31794             
31795             el.hide();
31796             
31797         }, this);
31798     },
31799     
31800     xhrOnLoad : function(xhr)
31801     {
31802         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31803             el.remove();
31804         }, this);
31805         
31806         if (xhr.readyState !== 4) {
31807             this.arrange();
31808             this.fireEvent('exception', this, xhr);
31809             return;
31810         }
31811
31812         var response = Roo.decode(xhr.responseText);
31813         
31814         if(!response.success){
31815             this.arrange();
31816             this.fireEvent('exception', this, xhr);
31817             return;
31818         }
31819         
31820         var file = this.renderPreview(response.data);
31821         
31822         this.files.push(file);
31823         
31824         this.arrange();
31825         
31826         this.fireEvent('afterupload', this, xhr);
31827         
31828     },
31829     
31830     xhrOnError : function(xhr)
31831     {
31832         Roo.log('xhr on error');
31833         
31834         var response = Roo.decode(xhr.responseText);
31835           
31836         Roo.log(response);
31837         
31838         this.arrange();
31839     },
31840     
31841     process : function(file)
31842     {
31843         if(this.fireEvent('process', this, file) !== false){
31844             if(this.editable && file.type.indexOf('image') != -1){
31845                 this.fireEvent('edit', this, file);
31846                 return;
31847             }
31848
31849             this.uploadStart(file, false);
31850
31851             return;
31852         }
31853         
31854     },
31855     
31856     uploadStart : function(file, crop)
31857     {
31858         this.xhr = new XMLHttpRequest();
31859         
31860         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31861             this.arrange();
31862             return;
31863         }
31864         
31865         file.xhr = this.xhr;
31866             
31867         this.managerEl.createChild({
31868             tag : 'div',
31869             cls : 'roo-document-manager-loading',
31870             cn : [
31871                 {
31872                     tag : 'div',
31873                     tooltip : file.name,
31874                     cls : 'roo-document-manager-thumb',
31875                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31876                 }
31877             ]
31878
31879         });
31880
31881         this.xhr.open(this.method, this.url, true);
31882         
31883         var headers = {
31884             "Accept": "application/json",
31885             "Cache-Control": "no-cache",
31886             "X-Requested-With": "XMLHttpRequest"
31887         };
31888         
31889         for (var headerName in headers) {
31890             var headerValue = headers[headerName];
31891             if (headerValue) {
31892                 this.xhr.setRequestHeader(headerName, headerValue);
31893             }
31894         }
31895         
31896         var _this = this;
31897         
31898         this.xhr.onload = function()
31899         {
31900             _this.xhrOnLoad(_this.xhr);
31901         }
31902         
31903         this.xhr.onerror = function()
31904         {
31905             _this.xhrOnError(_this.xhr);
31906         }
31907         
31908         var formData = new FormData();
31909
31910         formData.append('returnHTML', 'NO');
31911         
31912         if(crop){
31913             formData.append('crop', crop);
31914         }
31915         
31916         formData.append(this.paramName, file, file.name);
31917         
31918         var options = {
31919             file : file, 
31920             manually : false
31921         };
31922         
31923         if(this.fireEvent('prepare', this, formData, options) != false){
31924             
31925             if(options.manually){
31926                 return;
31927             }
31928             
31929             this.xhr.send(formData);
31930             return;
31931         };
31932         
31933         this.uploadCancel();
31934     },
31935     
31936     uploadCancel : function()
31937     {
31938         if (this.xhr) {
31939             this.xhr.abort();
31940         }
31941         
31942         this.delegates = [];
31943         
31944         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31945             el.remove();
31946         }, this);
31947         
31948         this.arrange();
31949     },
31950     
31951     renderPreview : function(file)
31952     {
31953         if(typeof(file.target) != 'undefined' && file.target){
31954             return file;
31955         }
31956         
31957         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31958         
31959         var previewEl = this.managerEl.createChild({
31960             tag : 'div',
31961             cls : 'roo-document-manager-preview',
31962             cn : [
31963                 {
31964                     tag : 'div',
31965                     tooltip : file[this.toolTipName],
31966                     cls : 'roo-document-manager-thumb',
31967                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31968                 },
31969                 {
31970                     tag : 'button',
31971                     cls : 'close',
31972                     html : '<i class="fa fa-times-circle"></i>'
31973                 }
31974             ]
31975         });
31976
31977         var close = previewEl.select('button.close', true).first();
31978
31979         close.on('click', this.onRemove, this, file);
31980
31981         file.target = previewEl;
31982
31983         var image = previewEl.select('img', true).first();
31984         
31985         var _this = this;
31986         
31987         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31988         
31989         image.on('click', this.onClick, this, file);
31990         
31991         this.fireEvent('previewrendered', this, file);
31992         
31993         return file;
31994         
31995     },
31996     
31997     onPreviewLoad : function(file, image)
31998     {
31999         if(typeof(file.target) == 'undefined' || !file.target){
32000             return;
32001         }
32002         
32003         var width = image.dom.naturalWidth || image.dom.width;
32004         var height = image.dom.naturalHeight || image.dom.height;
32005         
32006         if(!this.previewResize) {
32007             return;
32008         }
32009         
32010         if(width > height){
32011             file.target.addClass('wide');
32012             return;
32013         }
32014         
32015         file.target.addClass('tall');
32016         return;
32017         
32018     },
32019     
32020     uploadFromSource : function(file, crop)
32021     {
32022         this.xhr = new XMLHttpRequest();
32023         
32024         this.managerEl.createChild({
32025             tag : 'div',
32026             cls : 'roo-document-manager-loading',
32027             cn : [
32028                 {
32029                     tag : 'div',
32030                     tooltip : file.name,
32031                     cls : 'roo-document-manager-thumb',
32032                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32033                 }
32034             ]
32035
32036         });
32037
32038         this.xhr.open(this.method, this.url, true);
32039         
32040         var headers = {
32041             "Accept": "application/json",
32042             "Cache-Control": "no-cache",
32043             "X-Requested-With": "XMLHttpRequest"
32044         };
32045         
32046         for (var headerName in headers) {
32047             var headerValue = headers[headerName];
32048             if (headerValue) {
32049                 this.xhr.setRequestHeader(headerName, headerValue);
32050             }
32051         }
32052         
32053         var _this = this;
32054         
32055         this.xhr.onload = function()
32056         {
32057             _this.xhrOnLoad(_this.xhr);
32058         }
32059         
32060         this.xhr.onerror = function()
32061         {
32062             _this.xhrOnError(_this.xhr);
32063         }
32064         
32065         var formData = new FormData();
32066
32067         formData.append('returnHTML', 'NO');
32068         
32069         formData.append('crop', crop);
32070         
32071         if(typeof(file.filename) != 'undefined'){
32072             formData.append('filename', file.filename);
32073         }
32074         
32075         if(typeof(file.mimetype) != 'undefined'){
32076             formData.append('mimetype', file.mimetype);
32077         }
32078         
32079         Roo.log(formData);
32080         
32081         if(this.fireEvent('prepare', this, formData) != false){
32082             this.xhr.send(formData);
32083         };
32084     }
32085 });
32086
32087 /*
32088 * Licence: LGPL
32089 */
32090
32091 /**
32092  * @class Roo.bootstrap.DocumentViewer
32093  * @extends Roo.bootstrap.Component
32094  * Bootstrap DocumentViewer class
32095  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32096  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32097  * 
32098  * @constructor
32099  * Create a new DocumentViewer
32100  * @param {Object} config The config object
32101  */
32102
32103 Roo.bootstrap.DocumentViewer = function(config){
32104     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32105     
32106     this.addEvents({
32107         /**
32108          * @event initial
32109          * Fire after initEvent
32110          * @param {Roo.bootstrap.DocumentViewer} this
32111          */
32112         "initial" : true,
32113         /**
32114          * @event click
32115          * Fire after click
32116          * @param {Roo.bootstrap.DocumentViewer} this
32117          */
32118         "click" : true,
32119         /**
32120          * @event download
32121          * Fire after download button
32122          * @param {Roo.bootstrap.DocumentViewer} this
32123          */
32124         "download" : true,
32125         /**
32126          * @event trash
32127          * Fire after trash button
32128          * @param {Roo.bootstrap.DocumentViewer} this
32129          */
32130         "trash" : true
32131         
32132     });
32133 };
32134
32135 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32136     
32137     showDownload : true,
32138     
32139     showTrash : true,
32140     
32141     getAutoCreate : function()
32142     {
32143         var cfg = {
32144             tag : 'div',
32145             cls : 'roo-document-viewer',
32146             cn : [
32147                 {
32148                     tag : 'div',
32149                     cls : 'roo-document-viewer-body',
32150                     cn : [
32151                         {
32152                             tag : 'div',
32153                             cls : 'roo-document-viewer-thumb',
32154                             cn : [
32155                                 {
32156                                     tag : 'img',
32157                                     cls : 'roo-document-viewer-image'
32158                                 }
32159                             ]
32160                         }
32161                     ]
32162                 },
32163                 {
32164                     tag : 'div',
32165                     cls : 'roo-document-viewer-footer',
32166                     cn : {
32167                         tag : 'div',
32168                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32169                         cn : [
32170                             {
32171                                 tag : 'div',
32172                                 cls : 'btn-group roo-document-viewer-download',
32173                                 cn : [
32174                                     {
32175                                         tag : 'button',
32176                                         cls : 'btn btn-default',
32177                                         html : '<i class="fa fa-download"></i>'
32178                                     }
32179                                 ]
32180                             },
32181                             {
32182                                 tag : 'div',
32183                                 cls : 'btn-group roo-document-viewer-trash',
32184                                 cn : [
32185                                     {
32186                                         tag : 'button',
32187                                         cls : 'btn btn-default',
32188                                         html : '<i class="fa fa-trash"></i>'
32189                                     }
32190                                 ]
32191                             }
32192                         ]
32193                     }
32194                 }
32195             ]
32196         };
32197         
32198         return cfg;
32199     },
32200     
32201     initEvents : function()
32202     {
32203         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32204         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32205         
32206         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32207         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32208         
32209         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32210         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32211         
32212         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32213         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32214         
32215         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32216         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32217         
32218         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32219         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32220         
32221         this.bodyEl.on('click', this.onClick, this);
32222         this.downloadBtn.on('click', this.onDownload, this);
32223         this.trashBtn.on('click', this.onTrash, this);
32224         
32225         this.downloadBtn.hide();
32226         this.trashBtn.hide();
32227         
32228         if(this.showDownload){
32229             this.downloadBtn.show();
32230         }
32231         
32232         if(this.showTrash){
32233             this.trashBtn.show();
32234         }
32235         
32236         if(!this.showDownload && !this.showTrash) {
32237             this.footerEl.hide();
32238         }
32239         
32240     },
32241     
32242     initial : function()
32243     {
32244         this.fireEvent('initial', this);
32245         
32246     },
32247     
32248     onClick : function(e)
32249     {
32250         e.preventDefault();
32251         
32252         this.fireEvent('click', this);
32253     },
32254     
32255     onDownload : function(e)
32256     {
32257         e.preventDefault();
32258         
32259         this.fireEvent('download', this);
32260     },
32261     
32262     onTrash : function(e)
32263     {
32264         e.preventDefault();
32265         
32266         this.fireEvent('trash', this);
32267     }
32268     
32269 });
32270 /*
32271  * - LGPL
32272  *
32273  * nav progress bar
32274  * 
32275  */
32276
32277 /**
32278  * @class Roo.bootstrap.NavProgressBar
32279  * @extends Roo.bootstrap.Component
32280  * Bootstrap NavProgressBar class
32281  * 
32282  * @constructor
32283  * Create a new nav progress bar
32284  * @param {Object} config The config object
32285  */
32286
32287 Roo.bootstrap.NavProgressBar = function(config){
32288     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32289
32290     this.bullets = this.bullets || [];
32291    
32292 //    Roo.bootstrap.NavProgressBar.register(this);
32293      this.addEvents({
32294         /**
32295              * @event changed
32296              * Fires when the active item changes
32297              * @param {Roo.bootstrap.NavProgressBar} this
32298              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32299              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32300          */
32301         'changed': true
32302      });
32303     
32304 };
32305
32306 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32307     
32308     bullets : [],
32309     barItems : [],
32310     
32311     getAutoCreate : function()
32312     {
32313         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32314         
32315         cfg = {
32316             tag : 'div',
32317             cls : 'roo-navigation-bar-group',
32318             cn : [
32319                 {
32320                     tag : 'div',
32321                     cls : 'roo-navigation-top-bar'
32322                 },
32323                 {
32324                     tag : 'div',
32325                     cls : 'roo-navigation-bullets-bar',
32326                     cn : [
32327                         {
32328                             tag : 'ul',
32329                             cls : 'roo-navigation-bar'
32330                         }
32331                     ]
32332                 },
32333                 
32334                 {
32335                     tag : 'div',
32336                     cls : 'roo-navigation-bottom-bar'
32337                 }
32338             ]
32339             
32340         };
32341         
32342         return cfg;
32343         
32344     },
32345     
32346     initEvents: function() 
32347     {
32348         
32349     },
32350     
32351     onRender : function(ct, position) 
32352     {
32353         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32354         
32355         if(this.bullets.length){
32356             Roo.each(this.bullets, function(b){
32357                this.addItem(b);
32358             }, this);
32359         }
32360         
32361         this.format();
32362         
32363     },
32364     
32365     addItem : function(cfg)
32366     {
32367         var item = new Roo.bootstrap.NavProgressItem(cfg);
32368         
32369         item.parentId = this.id;
32370         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32371         
32372         if(cfg.html){
32373             var top = new Roo.bootstrap.Element({
32374                 tag : 'div',
32375                 cls : 'roo-navigation-bar-text'
32376             });
32377             
32378             var bottom = new Roo.bootstrap.Element({
32379                 tag : 'div',
32380                 cls : 'roo-navigation-bar-text'
32381             });
32382             
32383             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32384             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32385             
32386             var topText = new Roo.bootstrap.Element({
32387                 tag : 'span',
32388                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32389             });
32390             
32391             var bottomText = new Roo.bootstrap.Element({
32392                 tag : 'span',
32393                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32394             });
32395             
32396             topText.onRender(top.el, null);
32397             bottomText.onRender(bottom.el, null);
32398             
32399             item.topEl = top;
32400             item.bottomEl = bottom;
32401         }
32402         
32403         this.barItems.push(item);
32404         
32405         return item;
32406     },
32407     
32408     getActive : function()
32409     {
32410         var active = false;
32411         
32412         Roo.each(this.barItems, function(v){
32413             
32414             if (!v.isActive()) {
32415                 return;
32416             }
32417             
32418             active = v;
32419             return false;
32420             
32421         });
32422         
32423         return active;
32424     },
32425     
32426     setActiveItem : function(item)
32427     {
32428         var prev = false;
32429         
32430         Roo.each(this.barItems, function(v){
32431             if (v.rid == item.rid) {
32432                 return ;
32433             }
32434             
32435             if (v.isActive()) {
32436                 v.setActive(false);
32437                 prev = v;
32438             }
32439         });
32440
32441         item.setActive(true);
32442         
32443         this.fireEvent('changed', this, item, prev);
32444     },
32445     
32446     getBarItem: function(rid)
32447     {
32448         var ret = false;
32449         
32450         Roo.each(this.barItems, function(e) {
32451             if (e.rid != rid) {
32452                 return;
32453             }
32454             
32455             ret =  e;
32456             return false;
32457         });
32458         
32459         return ret;
32460     },
32461     
32462     indexOfItem : function(item)
32463     {
32464         var index = false;
32465         
32466         Roo.each(this.barItems, function(v, i){
32467             
32468             if (v.rid != item.rid) {
32469                 return;
32470             }
32471             
32472             index = i;
32473             return false
32474         });
32475         
32476         return index;
32477     },
32478     
32479     setActiveNext : function()
32480     {
32481         var i = this.indexOfItem(this.getActive());
32482         
32483         if (i > this.barItems.length) {
32484             return;
32485         }
32486         
32487         this.setActiveItem(this.barItems[i+1]);
32488     },
32489     
32490     setActivePrev : function()
32491     {
32492         var i = this.indexOfItem(this.getActive());
32493         
32494         if (i  < 1) {
32495             return;
32496         }
32497         
32498         this.setActiveItem(this.barItems[i-1]);
32499     },
32500     
32501     format : function()
32502     {
32503         if(!this.barItems.length){
32504             return;
32505         }
32506      
32507         var width = 100 / this.barItems.length;
32508         
32509         Roo.each(this.barItems, function(i){
32510             i.el.setStyle('width', width + '%');
32511             i.topEl.el.setStyle('width', width + '%');
32512             i.bottomEl.el.setStyle('width', width + '%');
32513         }, this);
32514         
32515     }
32516     
32517 });
32518 /*
32519  * - LGPL
32520  *
32521  * Nav Progress Item
32522  * 
32523  */
32524
32525 /**
32526  * @class Roo.bootstrap.NavProgressItem
32527  * @extends Roo.bootstrap.Component
32528  * Bootstrap NavProgressItem class
32529  * @cfg {String} rid the reference id
32530  * @cfg {Boolean} active (true|false) Is item active default false
32531  * @cfg {Boolean} disabled (true|false) Is item active default false
32532  * @cfg {String} html
32533  * @cfg {String} position (top|bottom) text position default bottom
32534  * @cfg {String} icon show icon instead of number
32535  * 
32536  * @constructor
32537  * Create a new NavProgressItem
32538  * @param {Object} config The config object
32539  */
32540 Roo.bootstrap.NavProgressItem = function(config){
32541     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32542     this.addEvents({
32543         // raw events
32544         /**
32545          * @event click
32546          * The raw click event for the entire grid.
32547          * @param {Roo.bootstrap.NavProgressItem} this
32548          * @param {Roo.EventObject} e
32549          */
32550         "click" : true
32551     });
32552    
32553 };
32554
32555 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32556     
32557     rid : '',
32558     active : false,
32559     disabled : false,
32560     html : '',
32561     position : 'bottom',
32562     icon : false,
32563     
32564     getAutoCreate : function()
32565     {
32566         var iconCls = 'roo-navigation-bar-item-icon';
32567         
32568         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32569         
32570         var cfg = {
32571             tag: 'li',
32572             cls: 'roo-navigation-bar-item',
32573             cn : [
32574                 {
32575                     tag : 'i',
32576                     cls : iconCls
32577                 }
32578             ]
32579         };
32580         
32581         if(this.active){
32582             cfg.cls += ' active';
32583         }
32584         if(this.disabled){
32585             cfg.cls += ' disabled';
32586         }
32587         
32588         return cfg;
32589     },
32590     
32591     disable : function()
32592     {
32593         this.setDisabled(true);
32594     },
32595     
32596     enable : function()
32597     {
32598         this.setDisabled(false);
32599     },
32600     
32601     initEvents: function() 
32602     {
32603         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32604         
32605         this.iconEl.on('click', this.onClick, this);
32606     },
32607     
32608     onClick : function(e)
32609     {
32610         e.preventDefault();
32611         
32612         if(this.disabled){
32613             return;
32614         }
32615         
32616         if(this.fireEvent('click', this, e) === false){
32617             return;
32618         };
32619         
32620         this.parent().setActiveItem(this);
32621     },
32622     
32623     isActive: function () 
32624     {
32625         return this.active;
32626     },
32627     
32628     setActive : function(state)
32629     {
32630         if(this.active == state){
32631             return;
32632         }
32633         
32634         this.active = state;
32635         
32636         if (state) {
32637             this.el.addClass('active');
32638             return;
32639         }
32640         
32641         this.el.removeClass('active');
32642         
32643         return;
32644     },
32645     
32646     setDisabled : function(state)
32647     {
32648         if(this.disabled == state){
32649             return;
32650         }
32651         
32652         this.disabled = state;
32653         
32654         if (state) {
32655             this.el.addClass('disabled');
32656             return;
32657         }
32658         
32659         this.el.removeClass('disabled');
32660     },
32661     
32662     tooltipEl : function()
32663     {
32664         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32665     }
32666 });
32667  
32668
32669  /*
32670  * - LGPL
32671  *
32672  * FieldLabel
32673  * 
32674  */
32675
32676 /**
32677  * @class Roo.bootstrap.FieldLabel
32678  * @extends Roo.bootstrap.Component
32679  * Bootstrap FieldLabel class
32680  * @cfg {String} html contents of the element
32681  * @cfg {String} tag tag of the element default label
32682  * @cfg {String} cls class of the element
32683  * @cfg {String} target label target 
32684  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32685  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32686  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32687  * @cfg {String} iconTooltip default "This field is required"
32688  * @cfg {String} indicatorpos (left|right) default left
32689  * 
32690  * @constructor
32691  * Create a new FieldLabel
32692  * @param {Object} config The config object
32693  */
32694
32695 Roo.bootstrap.FieldLabel = function(config){
32696     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32697     
32698     this.addEvents({
32699             /**
32700              * @event invalid
32701              * Fires after the field has been marked as invalid.
32702              * @param {Roo.form.FieldLabel} this
32703              * @param {String} msg The validation message
32704              */
32705             invalid : true,
32706             /**
32707              * @event valid
32708              * Fires after the field has been validated with no errors.
32709              * @param {Roo.form.FieldLabel} this
32710              */
32711             valid : true
32712         });
32713 };
32714
32715 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32716     
32717     tag: 'label',
32718     cls: '',
32719     html: '',
32720     target: '',
32721     allowBlank : true,
32722     invalidClass : 'has-warning',
32723     validClass : 'has-success',
32724     iconTooltip : 'This field is required',
32725     indicatorpos : 'left',
32726     
32727     getAutoCreate : function(){
32728         
32729         var cls = "";
32730         if (!this.allowBlank) {
32731             cls  = "visible";
32732         }
32733         
32734         var cfg = {
32735             tag : this.tag,
32736             cls : 'roo-bootstrap-field-label ' + this.cls,
32737             for : this.target,
32738             cn : [
32739                 {
32740                     tag : 'i',
32741                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32742                     tooltip : this.iconTooltip
32743                 },
32744                 {
32745                     tag : 'span',
32746                     html : this.html
32747                 }
32748             ] 
32749         };
32750         
32751         if(this.indicatorpos == 'right'){
32752             var cfg = {
32753                 tag : this.tag,
32754                 cls : 'roo-bootstrap-field-label ' + this.cls,
32755                 for : this.target,
32756                 cn : [
32757                     {
32758                         tag : 'span',
32759                         html : this.html
32760                     },
32761                     {
32762                         tag : 'i',
32763                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32764                         tooltip : this.iconTooltip
32765                     }
32766                 ] 
32767             };
32768         }
32769         
32770         return cfg;
32771     },
32772     
32773     initEvents: function() 
32774     {
32775         Roo.bootstrap.Element.superclass.initEvents.call(this);
32776         
32777         this.indicator = this.indicatorEl();
32778         
32779         if(this.indicator){
32780             this.indicator.removeClass('visible');
32781             this.indicator.addClass('invisible');
32782         }
32783         
32784         Roo.bootstrap.FieldLabel.register(this);
32785     },
32786     
32787     indicatorEl : function()
32788     {
32789         var indicator = this.el.select('i.roo-required-indicator',true).first();
32790         
32791         if(!indicator){
32792             return false;
32793         }
32794         
32795         return indicator;
32796         
32797     },
32798     
32799     /**
32800      * Mark this field as valid
32801      */
32802     markValid : function()
32803     {
32804         if(this.indicator){
32805             this.indicator.removeClass('visible');
32806             this.indicator.addClass('invisible');
32807         }
32808         if (Roo.bootstrap.version == 3) {
32809             this.el.removeClass(this.invalidClass);
32810             this.el.addClass(this.validClass);
32811         } else {
32812             this.el.removeClass('is-invalid');
32813             this.el.addClass('is-valid');
32814         }
32815         
32816         
32817         this.fireEvent('valid', this);
32818     },
32819     
32820     /**
32821      * Mark this field as invalid
32822      * @param {String} msg The validation message
32823      */
32824     markInvalid : function(msg)
32825     {
32826         if(this.indicator){
32827             this.indicator.removeClass('invisible');
32828             this.indicator.addClass('visible');
32829         }
32830           if (Roo.bootstrap.version == 3) {
32831             this.el.removeClass(this.validClass);
32832             this.el.addClass(this.invalidClass);
32833         } else {
32834             this.el.removeClass('is-valid');
32835             this.el.addClass('is-invalid');
32836         }
32837         
32838         
32839         this.fireEvent('invalid', this, msg);
32840     }
32841     
32842    
32843 });
32844
32845 Roo.apply(Roo.bootstrap.FieldLabel, {
32846     
32847     groups: {},
32848     
32849      /**
32850     * register a FieldLabel Group
32851     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32852     */
32853     register : function(label)
32854     {
32855         if(this.groups.hasOwnProperty(label.target)){
32856             return;
32857         }
32858      
32859         this.groups[label.target] = label;
32860         
32861     },
32862     /**
32863     * fetch a FieldLabel Group based on the target
32864     * @param {string} target
32865     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32866     */
32867     get: function(target) {
32868         if (typeof(this.groups[target]) == 'undefined') {
32869             return false;
32870         }
32871         
32872         return this.groups[target] ;
32873     }
32874 });
32875
32876  
32877
32878  /*
32879  * - LGPL
32880  *
32881  * page DateSplitField.
32882  * 
32883  */
32884
32885
32886 /**
32887  * @class Roo.bootstrap.DateSplitField
32888  * @extends Roo.bootstrap.Component
32889  * Bootstrap DateSplitField class
32890  * @cfg {string} fieldLabel - the label associated
32891  * @cfg {Number} labelWidth set the width of label (0-12)
32892  * @cfg {String} labelAlign (top|left)
32893  * @cfg {Boolean} dayAllowBlank (true|false) default false
32894  * @cfg {Boolean} monthAllowBlank (true|false) default false
32895  * @cfg {Boolean} yearAllowBlank (true|false) default false
32896  * @cfg {string} dayPlaceholder 
32897  * @cfg {string} monthPlaceholder
32898  * @cfg {string} yearPlaceholder
32899  * @cfg {string} dayFormat default 'd'
32900  * @cfg {string} monthFormat default 'm'
32901  * @cfg {string} yearFormat default 'Y'
32902  * @cfg {Number} labellg set the width of label (1-12)
32903  * @cfg {Number} labelmd set the width of label (1-12)
32904  * @cfg {Number} labelsm set the width of label (1-12)
32905  * @cfg {Number} labelxs set the width of label (1-12)
32906
32907  *     
32908  * @constructor
32909  * Create a new DateSplitField
32910  * @param {Object} config The config object
32911  */
32912
32913 Roo.bootstrap.DateSplitField = function(config){
32914     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32915     
32916     this.addEvents({
32917         // raw events
32918          /**
32919          * @event years
32920          * getting the data of years
32921          * @param {Roo.bootstrap.DateSplitField} this
32922          * @param {Object} years
32923          */
32924         "years" : true,
32925         /**
32926          * @event days
32927          * getting the data of days
32928          * @param {Roo.bootstrap.DateSplitField} this
32929          * @param {Object} days
32930          */
32931         "days" : true,
32932         /**
32933          * @event invalid
32934          * Fires after the field has been marked as invalid.
32935          * @param {Roo.form.Field} this
32936          * @param {String} msg The validation message
32937          */
32938         invalid : true,
32939        /**
32940          * @event valid
32941          * Fires after the field has been validated with no errors.
32942          * @param {Roo.form.Field} this
32943          */
32944         valid : true
32945     });
32946 };
32947
32948 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32949     
32950     fieldLabel : '',
32951     labelAlign : 'top',
32952     labelWidth : 3,
32953     dayAllowBlank : false,
32954     monthAllowBlank : false,
32955     yearAllowBlank : false,
32956     dayPlaceholder : '',
32957     monthPlaceholder : '',
32958     yearPlaceholder : '',
32959     dayFormat : 'd',
32960     monthFormat : 'm',
32961     yearFormat : 'Y',
32962     isFormField : true,
32963     labellg : 0,
32964     labelmd : 0,
32965     labelsm : 0,
32966     labelxs : 0,
32967     
32968     getAutoCreate : function()
32969     {
32970         var cfg = {
32971             tag : 'div',
32972             cls : 'row roo-date-split-field-group',
32973             cn : [
32974                 {
32975                     tag : 'input',
32976                     type : 'hidden',
32977                     cls : 'form-hidden-field roo-date-split-field-group-value',
32978                     name : this.name
32979                 }
32980             ]
32981         };
32982         
32983         var labelCls = 'col-md-12';
32984         var contentCls = 'col-md-4';
32985         
32986         if(this.fieldLabel){
32987             
32988             var label = {
32989                 tag : 'div',
32990                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32991                 cn : [
32992                     {
32993                         tag : 'label',
32994                         html : this.fieldLabel
32995                     }
32996                 ]
32997             };
32998             
32999             if(this.labelAlign == 'left'){
33000             
33001                 if(this.labelWidth > 12){
33002                     label.style = "width: " + this.labelWidth + 'px';
33003                 }
33004
33005                 if(this.labelWidth < 13 && this.labelmd == 0){
33006                     this.labelmd = this.labelWidth;
33007                 }
33008
33009                 if(this.labellg > 0){
33010                     labelCls = ' col-lg-' + this.labellg;
33011                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33012                 }
33013
33014                 if(this.labelmd > 0){
33015                     labelCls = ' col-md-' + this.labelmd;
33016                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33017                 }
33018
33019                 if(this.labelsm > 0){
33020                     labelCls = ' col-sm-' + this.labelsm;
33021                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33022                 }
33023
33024                 if(this.labelxs > 0){
33025                     labelCls = ' col-xs-' + this.labelxs;
33026                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33027                 }
33028             }
33029             
33030             label.cls += ' ' + labelCls;
33031             
33032             cfg.cn.push(label);
33033         }
33034         
33035         Roo.each(['day', 'month', 'year'], function(t){
33036             cfg.cn.push({
33037                 tag : 'div',
33038                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33039             });
33040         }, this);
33041         
33042         return cfg;
33043     },
33044     
33045     inputEl: function ()
33046     {
33047         return this.el.select('.roo-date-split-field-group-value', true).first();
33048     },
33049     
33050     onRender : function(ct, position) 
33051     {
33052         var _this = this;
33053         
33054         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33055         
33056         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33057         
33058         this.dayField = new Roo.bootstrap.ComboBox({
33059             allowBlank : this.dayAllowBlank,
33060             alwaysQuery : true,
33061             displayField : 'value',
33062             editable : false,
33063             fieldLabel : '',
33064             forceSelection : true,
33065             mode : 'local',
33066             placeholder : this.dayPlaceholder,
33067             selectOnFocus : true,
33068             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33069             triggerAction : 'all',
33070             typeAhead : true,
33071             valueField : 'value',
33072             store : new Roo.data.SimpleStore({
33073                 data : (function() {    
33074                     var days = [];
33075                     _this.fireEvent('days', _this, days);
33076                     return days;
33077                 })(),
33078                 fields : [ 'value' ]
33079             }),
33080             listeners : {
33081                 select : function (_self, record, index)
33082                 {
33083                     _this.setValue(_this.getValue());
33084                 }
33085             }
33086         });
33087
33088         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33089         
33090         this.monthField = new Roo.bootstrap.MonthField({
33091             after : '<i class=\"fa fa-calendar\"></i>',
33092             allowBlank : this.monthAllowBlank,
33093             placeholder : this.monthPlaceholder,
33094             readOnly : true,
33095             listeners : {
33096                 render : function (_self)
33097                 {
33098                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33099                         e.preventDefault();
33100                         _self.focus();
33101                     });
33102                 },
33103                 select : function (_self, oldvalue, newvalue)
33104                 {
33105                     _this.setValue(_this.getValue());
33106                 }
33107             }
33108         });
33109         
33110         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33111         
33112         this.yearField = new Roo.bootstrap.ComboBox({
33113             allowBlank : this.yearAllowBlank,
33114             alwaysQuery : true,
33115             displayField : 'value',
33116             editable : false,
33117             fieldLabel : '',
33118             forceSelection : true,
33119             mode : 'local',
33120             placeholder : this.yearPlaceholder,
33121             selectOnFocus : true,
33122             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33123             triggerAction : 'all',
33124             typeAhead : true,
33125             valueField : 'value',
33126             store : new Roo.data.SimpleStore({
33127                 data : (function() {
33128                     var years = [];
33129                     _this.fireEvent('years', _this, years);
33130                     return years;
33131                 })(),
33132                 fields : [ 'value' ]
33133             }),
33134             listeners : {
33135                 select : function (_self, record, index)
33136                 {
33137                     _this.setValue(_this.getValue());
33138                 }
33139             }
33140         });
33141
33142         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33143     },
33144     
33145     setValue : function(v, format)
33146     {
33147         this.inputEl.dom.value = v;
33148         
33149         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33150         
33151         var d = Date.parseDate(v, f);
33152         
33153         if(!d){
33154             this.validate();
33155             return;
33156         }
33157         
33158         this.setDay(d.format(this.dayFormat));
33159         this.setMonth(d.format(this.monthFormat));
33160         this.setYear(d.format(this.yearFormat));
33161         
33162         this.validate();
33163         
33164         return;
33165     },
33166     
33167     setDay : function(v)
33168     {
33169         this.dayField.setValue(v);
33170         this.inputEl.dom.value = this.getValue();
33171         this.validate();
33172         return;
33173     },
33174     
33175     setMonth : function(v)
33176     {
33177         this.monthField.setValue(v, true);
33178         this.inputEl.dom.value = this.getValue();
33179         this.validate();
33180         return;
33181     },
33182     
33183     setYear : function(v)
33184     {
33185         this.yearField.setValue(v);
33186         this.inputEl.dom.value = this.getValue();
33187         this.validate();
33188         return;
33189     },
33190     
33191     getDay : function()
33192     {
33193         return this.dayField.getValue();
33194     },
33195     
33196     getMonth : function()
33197     {
33198         return this.monthField.getValue();
33199     },
33200     
33201     getYear : function()
33202     {
33203         return this.yearField.getValue();
33204     },
33205     
33206     getValue : function()
33207     {
33208         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33209         
33210         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33211         
33212         return date;
33213     },
33214     
33215     reset : function()
33216     {
33217         this.setDay('');
33218         this.setMonth('');
33219         this.setYear('');
33220         this.inputEl.dom.value = '';
33221         this.validate();
33222         return;
33223     },
33224     
33225     validate : function()
33226     {
33227         var d = this.dayField.validate();
33228         var m = this.monthField.validate();
33229         var y = this.yearField.validate();
33230         
33231         var valid = true;
33232         
33233         if(
33234                 (!this.dayAllowBlank && !d) ||
33235                 (!this.monthAllowBlank && !m) ||
33236                 (!this.yearAllowBlank && !y)
33237         ){
33238             valid = false;
33239         }
33240         
33241         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33242             return valid;
33243         }
33244         
33245         if(valid){
33246             this.markValid();
33247             return valid;
33248         }
33249         
33250         this.markInvalid();
33251         
33252         return valid;
33253     },
33254     
33255     markValid : function()
33256     {
33257         
33258         var label = this.el.select('label', true).first();
33259         var icon = this.el.select('i.fa-star', true).first();
33260
33261         if(label && icon){
33262             icon.remove();
33263         }
33264         
33265         this.fireEvent('valid', this);
33266     },
33267     
33268      /**
33269      * Mark this field as invalid
33270      * @param {String} msg The validation message
33271      */
33272     markInvalid : function(msg)
33273     {
33274         
33275         var label = this.el.select('label', true).first();
33276         var icon = this.el.select('i.fa-star', true).first();
33277
33278         if(label && !icon){
33279             this.el.select('.roo-date-split-field-label', true).createChild({
33280                 tag : 'i',
33281                 cls : 'text-danger fa fa-lg fa-star',
33282                 tooltip : 'This field is required',
33283                 style : 'margin-right:5px;'
33284             }, label, true);
33285         }
33286         
33287         this.fireEvent('invalid', this, msg);
33288     },
33289     
33290     clearInvalid : function()
33291     {
33292         var label = this.el.select('label', true).first();
33293         var icon = this.el.select('i.fa-star', true).first();
33294
33295         if(label && icon){
33296             icon.remove();
33297         }
33298         
33299         this.fireEvent('valid', this);
33300     },
33301     
33302     getName: function()
33303     {
33304         return this.name;
33305     }
33306     
33307 });
33308
33309  /**
33310  *
33311  * This is based on 
33312  * http://masonry.desandro.com
33313  *
33314  * The idea is to render all the bricks based on vertical width...
33315  *
33316  * The original code extends 'outlayer' - we might need to use that....
33317  * 
33318  */
33319
33320
33321 /**
33322  * @class Roo.bootstrap.LayoutMasonry
33323  * @extends Roo.bootstrap.Component
33324  * Bootstrap Layout Masonry class
33325  * 
33326  * @constructor
33327  * Create a new Element
33328  * @param {Object} config The config object
33329  */
33330
33331 Roo.bootstrap.LayoutMasonry = function(config){
33332     
33333     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33334     
33335     this.bricks = [];
33336     
33337     Roo.bootstrap.LayoutMasonry.register(this);
33338     
33339     this.addEvents({
33340         // raw events
33341         /**
33342          * @event layout
33343          * Fire after layout the items
33344          * @param {Roo.bootstrap.LayoutMasonry} this
33345          * @param {Roo.EventObject} e
33346          */
33347         "layout" : true
33348     });
33349     
33350 };
33351
33352 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33353     
33354     /**
33355      * @cfg {Boolean} isLayoutInstant = no animation?
33356      */   
33357     isLayoutInstant : false, // needed?
33358    
33359     /**
33360      * @cfg {Number} boxWidth  width of the columns
33361      */   
33362     boxWidth : 450,
33363     
33364       /**
33365      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33366      */   
33367     boxHeight : 0,
33368     
33369     /**
33370      * @cfg {Number} padWidth padding below box..
33371      */   
33372     padWidth : 10, 
33373     
33374     /**
33375      * @cfg {Number} gutter gutter width..
33376      */   
33377     gutter : 10,
33378     
33379      /**
33380      * @cfg {Number} maxCols maximum number of columns
33381      */   
33382     
33383     maxCols: 0,
33384     
33385     /**
33386      * @cfg {Boolean} isAutoInitial defalut true
33387      */   
33388     isAutoInitial : true, 
33389     
33390     containerWidth: 0,
33391     
33392     /**
33393      * @cfg {Boolean} isHorizontal defalut false
33394      */   
33395     isHorizontal : false, 
33396
33397     currentSize : null,
33398     
33399     tag: 'div',
33400     
33401     cls: '',
33402     
33403     bricks: null, //CompositeElement
33404     
33405     cols : 1,
33406     
33407     _isLayoutInited : false,
33408     
33409 //    isAlternative : false, // only use for vertical layout...
33410     
33411     /**
33412      * @cfg {Number} alternativePadWidth padding below box..
33413      */   
33414     alternativePadWidth : 50,
33415     
33416     selectedBrick : [],
33417     
33418     getAutoCreate : function(){
33419         
33420         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33421         
33422         var cfg = {
33423             tag: this.tag,
33424             cls: 'blog-masonary-wrapper ' + this.cls,
33425             cn : {
33426                 cls : 'mas-boxes masonary'
33427             }
33428         };
33429         
33430         return cfg;
33431     },
33432     
33433     getChildContainer: function( )
33434     {
33435         if (this.boxesEl) {
33436             return this.boxesEl;
33437         }
33438         
33439         this.boxesEl = this.el.select('.mas-boxes').first();
33440         
33441         return this.boxesEl;
33442     },
33443     
33444     
33445     initEvents : function()
33446     {
33447         var _this = this;
33448         
33449         if(this.isAutoInitial){
33450             Roo.log('hook children rendered');
33451             this.on('childrenrendered', function() {
33452                 Roo.log('children rendered');
33453                 _this.initial();
33454             } ,this);
33455         }
33456     },
33457     
33458     initial : function()
33459     {
33460         this.selectedBrick = [];
33461         
33462         this.currentSize = this.el.getBox(true);
33463         
33464         Roo.EventManager.onWindowResize(this.resize, this); 
33465
33466         if(!this.isAutoInitial){
33467             this.layout();
33468             return;
33469         }
33470         
33471         this.layout();
33472         
33473         return;
33474         //this.layout.defer(500,this);
33475         
33476     },
33477     
33478     resize : function()
33479     {
33480         var cs = this.el.getBox(true);
33481         
33482         if (
33483                 this.currentSize.width == cs.width && 
33484                 this.currentSize.x == cs.x && 
33485                 this.currentSize.height == cs.height && 
33486                 this.currentSize.y == cs.y 
33487         ) {
33488             Roo.log("no change in with or X or Y");
33489             return;
33490         }
33491         
33492         this.currentSize = cs;
33493         
33494         this.layout();
33495         
33496     },
33497     
33498     layout : function()
33499     {   
33500         this._resetLayout();
33501         
33502         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33503         
33504         this.layoutItems( isInstant );
33505       
33506         this._isLayoutInited = true;
33507         
33508         this.fireEvent('layout', this);
33509         
33510     },
33511     
33512     _resetLayout : function()
33513     {
33514         if(this.isHorizontal){
33515             this.horizontalMeasureColumns();
33516             return;
33517         }
33518         
33519         this.verticalMeasureColumns();
33520         
33521     },
33522     
33523     verticalMeasureColumns : function()
33524     {
33525         this.getContainerWidth();
33526         
33527 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33528 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33529 //            return;
33530 //        }
33531         
33532         var boxWidth = this.boxWidth + this.padWidth;
33533         
33534         if(this.containerWidth < this.boxWidth){
33535             boxWidth = this.containerWidth
33536         }
33537         
33538         var containerWidth = this.containerWidth;
33539         
33540         var cols = Math.floor(containerWidth / boxWidth);
33541         
33542         this.cols = Math.max( cols, 1 );
33543         
33544         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33545         
33546         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33547         
33548         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33549         
33550         this.colWidth = boxWidth + avail - this.padWidth;
33551         
33552         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33553         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33554     },
33555     
33556     horizontalMeasureColumns : function()
33557     {
33558         this.getContainerWidth();
33559         
33560         var boxWidth = this.boxWidth;
33561         
33562         if(this.containerWidth < boxWidth){
33563             boxWidth = this.containerWidth;
33564         }
33565         
33566         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33567         
33568         this.el.setHeight(boxWidth);
33569         
33570     },
33571     
33572     getContainerWidth : function()
33573     {
33574         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33575     },
33576     
33577     layoutItems : function( isInstant )
33578     {
33579         Roo.log(this.bricks);
33580         
33581         var items = Roo.apply([], this.bricks);
33582         
33583         if(this.isHorizontal){
33584             this._horizontalLayoutItems( items , isInstant );
33585             return;
33586         }
33587         
33588 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33589 //            this._verticalAlternativeLayoutItems( items , isInstant );
33590 //            return;
33591 //        }
33592         
33593         this._verticalLayoutItems( items , isInstant );
33594         
33595     },
33596     
33597     _verticalLayoutItems : function ( items , isInstant)
33598     {
33599         if ( !items || !items.length ) {
33600             return;
33601         }
33602         
33603         var standard = [
33604             ['xs', 'xs', 'xs', 'tall'],
33605             ['xs', 'xs', 'tall'],
33606             ['xs', 'xs', 'sm'],
33607             ['xs', 'xs', 'xs'],
33608             ['xs', 'tall'],
33609             ['xs', 'sm'],
33610             ['xs', 'xs'],
33611             ['xs'],
33612             
33613             ['sm', 'xs', 'xs'],
33614             ['sm', 'xs'],
33615             ['sm'],
33616             
33617             ['tall', 'xs', 'xs', 'xs'],
33618             ['tall', 'xs', 'xs'],
33619             ['tall', 'xs'],
33620             ['tall']
33621             
33622         ];
33623         
33624         var queue = [];
33625         
33626         var boxes = [];
33627         
33628         var box = [];
33629         
33630         Roo.each(items, function(item, k){
33631             
33632             switch (item.size) {
33633                 // these layouts take up a full box,
33634                 case 'md' :
33635                 case 'md-left' :
33636                 case 'md-right' :
33637                 case 'wide' :
33638                     
33639                     if(box.length){
33640                         boxes.push(box);
33641                         box = [];
33642                     }
33643                     
33644                     boxes.push([item]);
33645                     
33646                     break;
33647                     
33648                 case 'xs' :
33649                 case 'sm' :
33650                 case 'tall' :
33651                     
33652                     box.push(item);
33653                     
33654                     break;
33655                 default :
33656                     break;
33657                     
33658             }
33659             
33660         }, this);
33661         
33662         if(box.length){
33663             boxes.push(box);
33664             box = [];
33665         }
33666         
33667         var filterPattern = function(box, length)
33668         {
33669             if(!box.length){
33670                 return;
33671             }
33672             
33673             var match = false;
33674             
33675             var pattern = box.slice(0, length);
33676             
33677             var format = [];
33678             
33679             Roo.each(pattern, function(i){
33680                 format.push(i.size);
33681             }, this);
33682             
33683             Roo.each(standard, function(s){
33684                 
33685                 if(String(s) != String(format)){
33686                     return;
33687                 }
33688                 
33689                 match = true;
33690                 return false;
33691                 
33692             }, this);
33693             
33694             if(!match && length == 1){
33695                 return;
33696             }
33697             
33698             if(!match){
33699                 filterPattern(box, length - 1);
33700                 return;
33701             }
33702                 
33703             queue.push(pattern);
33704
33705             box = box.slice(length, box.length);
33706
33707             filterPattern(box, 4);
33708
33709             return;
33710             
33711         }
33712         
33713         Roo.each(boxes, function(box, k){
33714             
33715             if(!box.length){
33716                 return;
33717             }
33718             
33719             if(box.length == 1){
33720                 queue.push(box);
33721                 return;
33722             }
33723             
33724             filterPattern(box, 4);
33725             
33726         }, this);
33727         
33728         this._processVerticalLayoutQueue( queue, isInstant );
33729         
33730     },
33731     
33732 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33733 //    {
33734 //        if ( !items || !items.length ) {
33735 //            return;
33736 //        }
33737 //
33738 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33739 //        
33740 //    },
33741     
33742     _horizontalLayoutItems : function ( items , isInstant)
33743     {
33744         if ( !items || !items.length || items.length < 3) {
33745             return;
33746         }
33747         
33748         items.reverse();
33749         
33750         var eItems = items.slice(0, 3);
33751         
33752         items = items.slice(3, items.length);
33753         
33754         var standard = [
33755             ['xs', 'xs', 'xs', 'wide'],
33756             ['xs', 'xs', 'wide'],
33757             ['xs', 'xs', 'sm'],
33758             ['xs', 'xs', 'xs'],
33759             ['xs', 'wide'],
33760             ['xs', 'sm'],
33761             ['xs', 'xs'],
33762             ['xs'],
33763             
33764             ['sm', 'xs', 'xs'],
33765             ['sm', 'xs'],
33766             ['sm'],
33767             
33768             ['wide', 'xs', 'xs', 'xs'],
33769             ['wide', 'xs', 'xs'],
33770             ['wide', 'xs'],
33771             ['wide'],
33772             
33773             ['wide-thin']
33774         ];
33775         
33776         var queue = [];
33777         
33778         var boxes = [];
33779         
33780         var box = [];
33781         
33782         Roo.each(items, function(item, k){
33783             
33784             switch (item.size) {
33785                 case 'md' :
33786                 case 'md-left' :
33787                 case 'md-right' :
33788                 case 'tall' :
33789                     
33790                     if(box.length){
33791                         boxes.push(box);
33792                         box = [];
33793                     }
33794                     
33795                     boxes.push([item]);
33796                     
33797                     break;
33798                     
33799                 case 'xs' :
33800                 case 'sm' :
33801                 case 'wide' :
33802                 case 'wide-thin' :
33803                     
33804                     box.push(item);
33805                     
33806                     break;
33807                 default :
33808                     break;
33809                     
33810             }
33811             
33812         }, this);
33813         
33814         if(box.length){
33815             boxes.push(box);
33816             box = [];
33817         }
33818         
33819         var filterPattern = function(box, length)
33820         {
33821             if(!box.length){
33822                 return;
33823             }
33824             
33825             var match = false;
33826             
33827             var pattern = box.slice(0, length);
33828             
33829             var format = [];
33830             
33831             Roo.each(pattern, function(i){
33832                 format.push(i.size);
33833             }, this);
33834             
33835             Roo.each(standard, function(s){
33836                 
33837                 if(String(s) != String(format)){
33838                     return;
33839                 }
33840                 
33841                 match = true;
33842                 return false;
33843                 
33844             }, this);
33845             
33846             if(!match && length == 1){
33847                 return;
33848             }
33849             
33850             if(!match){
33851                 filterPattern(box, length - 1);
33852                 return;
33853             }
33854                 
33855             queue.push(pattern);
33856
33857             box = box.slice(length, box.length);
33858
33859             filterPattern(box, 4);
33860
33861             return;
33862             
33863         }
33864         
33865         Roo.each(boxes, function(box, k){
33866             
33867             if(!box.length){
33868                 return;
33869             }
33870             
33871             if(box.length == 1){
33872                 queue.push(box);
33873                 return;
33874             }
33875             
33876             filterPattern(box, 4);
33877             
33878         }, this);
33879         
33880         
33881         var prune = [];
33882         
33883         var pos = this.el.getBox(true);
33884         
33885         var minX = pos.x;
33886         
33887         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33888         
33889         var hit_end = false;
33890         
33891         Roo.each(queue, function(box){
33892             
33893             if(hit_end){
33894                 
33895                 Roo.each(box, function(b){
33896                 
33897                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33898                     b.el.hide();
33899
33900                 }, this);
33901
33902                 return;
33903             }
33904             
33905             var mx = 0;
33906             
33907             Roo.each(box, function(b){
33908                 
33909                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33910                 b.el.show();
33911
33912                 mx = Math.max(mx, b.x);
33913                 
33914             }, this);
33915             
33916             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33917             
33918             if(maxX < minX){
33919                 
33920                 Roo.each(box, function(b){
33921                 
33922                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33923                     b.el.hide();
33924                     
33925                 }, this);
33926                 
33927                 hit_end = true;
33928                 
33929                 return;
33930             }
33931             
33932             prune.push(box);
33933             
33934         }, this);
33935         
33936         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33937     },
33938     
33939     /** Sets position of item in DOM
33940     * @param {Element} item
33941     * @param {Number} x - horizontal position
33942     * @param {Number} y - vertical position
33943     * @param {Boolean} isInstant - disables transitions
33944     */
33945     _processVerticalLayoutQueue : function( queue, isInstant )
33946     {
33947         var pos = this.el.getBox(true);
33948         var x = pos.x;
33949         var y = pos.y;
33950         var maxY = [];
33951         
33952         for (var i = 0; i < this.cols; i++){
33953             maxY[i] = pos.y;
33954         }
33955         
33956         Roo.each(queue, function(box, k){
33957             
33958             var col = k % this.cols;
33959             
33960             Roo.each(box, function(b,kk){
33961                 
33962                 b.el.position('absolute');
33963                 
33964                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33965                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33966                 
33967                 if(b.size == 'md-left' || b.size == 'md-right'){
33968                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33969                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33970                 }
33971                 
33972                 b.el.setWidth(width);
33973                 b.el.setHeight(height);
33974                 // iframe?
33975                 b.el.select('iframe',true).setSize(width,height);
33976                 
33977             }, this);
33978             
33979             for (var i = 0; i < this.cols; i++){
33980                 
33981                 if(maxY[i] < maxY[col]){
33982                     col = i;
33983                     continue;
33984                 }
33985                 
33986                 col = Math.min(col, i);
33987                 
33988             }
33989             
33990             x = pos.x + col * (this.colWidth + this.padWidth);
33991             
33992             y = maxY[col];
33993             
33994             var positions = [];
33995             
33996             switch (box.length){
33997                 case 1 :
33998                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33999                     break;
34000                 case 2 :
34001                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34002                     break;
34003                 case 3 :
34004                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34005                     break;
34006                 case 4 :
34007                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34008                     break;
34009                 default :
34010                     break;
34011             }
34012             
34013             Roo.each(box, function(b,kk){
34014                 
34015                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34016                 
34017                 var sz = b.el.getSize();
34018                 
34019                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34020                 
34021             }, this);
34022             
34023         }, this);
34024         
34025         var mY = 0;
34026         
34027         for (var i = 0; i < this.cols; i++){
34028             mY = Math.max(mY, maxY[i]);
34029         }
34030         
34031         this.el.setHeight(mY - pos.y);
34032         
34033     },
34034     
34035 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34036 //    {
34037 //        var pos = this.el.getBox(true);
34038 //        var x = pos.x;
34039 //        var y = pos.y;
34040 //        var maxX = pos.right;
34041 //        
34042 //        var maxHeight = 0;
34043 //        
34044 //        Roo.each(items, function(item, k){
34045 //            
34046 //            var c = k % 2;
34047 //            
34048 //            item.el.position('absolute');
34049 //                
34050 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34051 //
34052 //            item.el.setWidth(width);
34053 //
34054 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34055 //
34056 //            item.el.setHeight(height);
34057 //            
34058 //            if(c == 0){
34059 //                item.el.setXY([x, y], isInstant ? false : true);
34060 //            } else {
34061 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34062 //            }
34063 //            
34064 //            y = y + height + this.alternativePadWidth;
34065 //            
34066 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34067 //            
34068 //        }, this);
34069 //        
34070 //        this.el.setHeight(maxHeight);
34071 //        
34072 //    },
34073     
34074     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34075     {
34076         var pos = this.el.getBox(true);
34077         
34078         var minX = pos.x;
34079         var minY = pos.y;
34080         
34081         var maxX = pos.right;
34082         
34083         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34084         
34085         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34086         
34087         Roo.each(queue, function(box, k){
34088             
34089             Roo.each(box, function(b, kk){
34090                 
34091                 b.el.position('absolute');
34092                 
34093                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34094                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34095                 
34096                 if(b.size == 'md-left' || b.size == 'md-right'){
34097                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34098                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34099                 }
34100                 
34101                 b.el.setWidth(width);
34102                 b.el.setHeight(height);
34103                 
34104             }, this);
34105             
34106             if(!box.length){
34107                 return;
34108             }
34109             
34110             var positions = [];
34111             
34112             switch (box.length){
34113                 case 1 :
34114                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34115                     break;
34116                 case 2 :
34117                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34118                     break;
34119                 case 3 :
34120                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34121                     break;
34122                 case 4 :
34123                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34124                     break;
34125                 default :
34126                     break;
34127             }
34128             
34129             Roo.each(box, function(b,kk){
34130                 
34131                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34132                 
34133                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34134                 
34135             }, this);
34136             
34137         }, this);
34138         
34139     },
34140     
34141     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34142     {
34143         Roo.each(eItems, function(b,k){
34144             
34145             b.size = (k == 0) ? 'sm' : 'xs';
34146             b.x = (k == 0) ? 2 : 1;
34147             b.y = (k == 0) ? 2 : 1;
34148             
34149             b.el.position('absolute');
34150             
34151             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34152                 
34153             b.el.setWidth(width);
34154             
34155             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34156             
34157             b.el.setHeight(height);
34158             
34159         }, this);
34160
34161         var positions = [];
34162         
34163         positions.push({
34164             x : maxX - this.unitWidth * 2 - this.gutter,
34165             y : minY
34166         });
34167         
34168         positions.push({
34169             x : maxX - this.unitWidth,
34170             y : minY + (this.unitWidth + this.gutter) * 2
34171         });
34172         
34173         positions.push({
34174             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34175             y : minY
34176         });
34177         
34178         Roo.each(eItems, function(b,k){
34179             
34180             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34181
34182         }, this);
34183         
34184     },
34185     
34186     getVerticalOneBoxColPositions : function(x, y, box)
34187     {
34188         var pos = [];
34189         
34190         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34191         
34192         if(box[0].size == 'md-left'){
34193             rand = 0;
34194         }
34195         
34196         if(box[0].size == 'md-right'){
34197             rand = 1;
34198         }
34199         
34200         pos.push({
34201             x : x + (this.unitWidth + this.gutter) * rand,
34202             y : y
34203         });
34204         
34205         return pos;
34206     },
34207     
34208     getVerticalTwoBoxColPositions : function(x, y, box)
34209     {
34210         var pos = [];
34211         
34212         if(box[0].size == 'xs'){
34213             
34214             pos.push({
34215                 x : x,
34216                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34217             });
34218
34219             pos.push({
34220                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34221                 y : y
34222             });
34223             
34224             return pos;
34225             
34226         }
34227         
34228         pos.push({
34229             x : x,
34230             y : y
34231         });
34232
34233         pos.push({
34234             x : x + (this.unitWidth + this.gutter) * 2,
34235             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34236         });
34237         
34238         return pos;
34239         
34240     },
34241     
34242     getVerticalThreeBoxColPositions : function(x, y, box)
34243     {
34244         var pos = [];
34245         
34246         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34247             
34248             pos.push({
34249                 x : x,
34250                 y : y
34251             });
34252
34253             pos.push({
34254                 x : x + (this.unitWidth + this.gutter) * 1,
34255                 y : y
34256             });
34257             
34258             pos.push({
34259                 x : x + (this.unitWidth + this.gutter) * 2,
34260                 y : y
34261             });
34262             
34263             return pos;
34264             
34265         }
34266         
34267         if(box[0].size == 'xs' && box[1].size == 'xs'){
34268             
34269             pos.push({
34270                 x : x,
34271                 y : y
34272             });
34273
34274             pos.push({
34275                 x : x,
34276                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34277             });
34278             
34279             pos.push({
34280                 x : x + (this.unitWidth + this.gutter) * 1,
34281                 y : y
34282             });
34283             
34284             return pos;
34285             
34286         }
34287         
34288         pos.push({
34289             x : x,
34290             y : y
34291         });
34292
34293         pos.push({
34294             x : x + (this.unitWidth + this.gutter) * 2,
34295             y : y
34296         });
34297
34298         pos.push({
34299             x : x + (this.unitWidth + this.gutter) * 2,
34300             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34301         });
34302             
34303         return pos;
34304         
34305     },
34306     
34307     getVerticalFourBoxColPositions : function(x, y, box)
34308     {
34309         var pos = [];
34310         
34311         if(box[0].size == 'xs'){
34312             
34313             pos.push({
34314                 x : x,
34315                 y : y
34316             });
34317
34318             pos.push({
34319                 x : x,
34320                 y : y + (this.unitHeight + this.gutter) * 1
34321             });
34322             
34323             pos.push({
34324                 x : x,
34325                 y : y + (this.unitHeight + this.gutter) * 2
34326             });
34327             
34328             pos.push({
34329                 x : x + (this.unitWidth + this.gutter) * 1,
34330                 y : y
34331             });
34332             
34333             return pos;
34334             
34335         }
34336         
34337         pos.push({
34338             x : x,
34339             y : y
34340         });
34341
34342         pos.push({
34343             x : x + (this.unitWidth + this.gutter) * 2,
34344             y : y
34345         });
34346
34347         pos.push({
34348             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34349             y : y + (this.unitHeight + this.gutter) * 1
34350         });
34351
34352         pos.push({
34353             x : x + (this.unitWidth + this.gutter) * 2,
34354             y : y + (this.unitWidth + this.gutter) * 2
34355         });
34356
34357         return pos;
34358         
34359     },
34360     
34361     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34362     {
34363         var pos = [];
34364         
34365         if(box[0].size == 'md-left'){
34366             pos.push({
34367                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34368                 y : minY
34369             });
34370             
34371             return pos;
34372         }
34373         
34374         if(box[0].size == 'md-right'){
34375             pos.push({
34376                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34377                 y : minY + (this.unitWidth + this.gutter) * 1
34378             });
34379             
34380             return pos;
34381         }
34382         
34383         var rand = Math.floor(Math.random() * (4 - box[0].y));
34384         
34385         pos.push({
34386             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34387             y : minY + (this.unitWidth + this.gutter) * rand
34388         });
34389         
34390         return pos;
34391         
34392     },
34393     
34394     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34395     {
34396         var pos = [];
34397         
34398         if(box[0].size == 'xs'){
34399             
34400             pos.push({
34401                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34402                 y : minY
34403             });
34404
34405             pos.push({
34406                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34407                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34408             });
34409             
34410             return pos;
34411             
34412         }
34413         
34414         pos.push({
34415             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34416             y : minY
34417         });
34418
34419         pos.push({
34420             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34421             y : minY + (this.unitWidth + this.gutter) * 2
34422         });
34423         
34424         return pos;
34425         
34426     },
34427     
34428     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34429     {
34430         var pos = [];
34431         
34432         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34433             
34434             pos.push({
34435                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34436                 y : minY
34437             });
34438
34439             pos.push({
34440                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34441                 y : minY + (this.unitWidth + this.gutter) * 1
34442             });
34443             
34444             pos.push({
34445                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34446                 y : minY + (this.unitWidth + this.gutter) * 2
34447             });
34448             
34449             return pos;
34450             
34451         }
34452         
34453         if(box[0].size == 'xs' && box[1].size == 'xs'){
34454             
34455             pos.push({
34456                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34457                 y : minY
34458             });
34459
34460             pos.push({
34461                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34462                 y : minY
34463             });
34464             
34465             pos.push({
34466                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34467                 y : minY + (this.unitWidth + this.gutter) * 1
34468             });
34469             
34470             return pos;
34471             
34472         }
34473         
34474         pos.push({
34475             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34476             y : minY
34477         });
34478
34479         pos.push({
34480             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34481             y : minY + (this.unitWidth + this.gutter) * 2
34482         });
34483
34484         pos.push({
34485             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34486             y : minY + (this.unitWidth + this.gutter) * 2
34487         });
34488             
34489         return pos;
34490         
34491     },
34492     
34493     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34494     {
34495         var pos = [];
34496         
34497         if(box[0].size == 'xs'){
34498             
34499             pos.push({
34500                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34501                 y : minY
34502             });
34503
34504             pos.push({
34505                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34506                 y : minY
34507             });
34508             
34509             pos.push({
34510                 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),
34511                 y : minY
34512             });
34513             
34514             pos.push({
34515                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34516                 y : minY + (this.unitWidth + this.gutter) * 1
34517             });
34518             
34519             return pos;
34520             
34521         }
34522         
34523         pos.push({
34524             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525             y : minY
34526         });
34527         
34528         pos.push({
34529             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34530             y : minY + (this.unitWidth + this.gutter) * 2
34531         });
34532         
34533         pos.push({
34534             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34535             y : minY + (this.unitWidth + this.gutter) * 2
34536         });
34537         
34538         pos.push({
34539             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),
34540             y : minY + (this.unitWidth + this.gutter) * 2
34541         });
34542
34543         return pos;
34544         
34545     },
34546     
34547     /**
34548     * remove a Masonry Brick
34549     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34550     */
34551     removeBrick : function(brick_id)
34552     {
34553         if (!brick_id) {
34554             return;
34555         }
34556         
34557         for (var i = 0; i<this.bricks.length; i++) {
34558             if (this.bricks[i].id == brick_id) {
34559                 this.bricks.splice(i,1);
34560                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34561                 this.initial();
34562             }
34563         }
34564     },
34565     
34566     /**
34567     * adds a Masonry Brick
34568     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34569     */
34570     addBrick : function(cfg)
34571     {
34572         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34573         //this.register(cn);
34574         cn.parentId = this.id;
34575         cn.render(this.el);
34576         return cn;
34577     },
34578     
34579     /**
34580     * register a Masonry Brick
34581     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34582     */
34583     
34584     register : function(brick)
34585     {
34586         this.bricks.push(brick);
34587         brick.masonryId = this.id;
34588     },
34589     
34590     /**
34591     * clear all the Masonry Brick
34592     */
34593     clearAll : function()
34594     {
34595         this.bricks = [];
34596         //this.getChildContainer().dom.innerHTML = "";
34597         this.el.dom.innerHTML = '';
34598     },
34599     
34600     getSelected : function()
34601     {
34602         if (!this.selectedBrick) {
34603             return false;
34604         }
34605         
34606         return this.selectedBrick;
34607     }
34608 });
34609
34610 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34611     
34612     groups: {},
34613      /**
34614     * register a Masonry Layout
34615     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34616     */
34617     
34618     register : function(layout)
34619     {
34620         this.groups[layout.id] = layout;
34621     },
34622     /**
34623     * fetch a  Masonry Layout based on the masonry layout ID
34624     * @param {string} the masonry layout to add
34625     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34626     */
34627     
34628     get: function(layout_id) {
34629         if (typeof(this.groups[layout_id]) == 'undefined') {
34630             return false;
34631         }
34632         return this.groups[layout_id] ;
34633     }
34634     
34635     
34636     
34637 });
34638
34639  
34640
34641  /**
34642  *
34643  * This is based on 
34644  * http://masonry.desandro.com
34645  *
34646  * The idea is to render all the bricks based on vertical width...
34647  *
34648  * The original code extends 'outlayer' - we might need to use that....
34649  * 
34650  */
34651
34652
34653 /**
34654  * @class Roo.bootstrap.LayoutMasonryAuto
34655  * @extends Roo.bootstrap.Component
34656  * Bootstrap Layout Masonry class
34657  * 
34658  * @constructor
34659  * Create a new Element
34660  * @param {Object} config The config object
34661  */
34662
34663 Roo.bootstrap.LayoutMasonryAuto = function(config){
34664     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34665 };
34666
34667 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34668     
34669       /**
34670      * @cfg {Boolean} isFitWidth  - resize the width..
34671      */   
34672     isFitWidth : false,  // options..
34673     /**
34674      * @cfg {Boolean} isOriginLeft = left align?
34675      */   
34676     isOriginLeft : true,
34677     /**
34678      * @cfg {Boolean} isOriginTop = top align?
34679      */   
34680     isOriginTop : false,
34681     /**
34682      * @cfg {Boolean} isLayoutInstant = no animation?
34683      */   
34684     isLayoutInstant : false, // needed?
34685     /**
34686      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34687      */   
34688     isResizingContainer : true,
34689     /**
34690      * @cfg {Number} columnWidth  width of the columns 
34691      */   
34692     
34693     columnWidth : 0,
34694     
34695     /**
34696      * @cfg {Number} maxCols maximum number of columns
34697      */   
34698     
34699     maxCols: 0,
34700     /**
34701      * @cfg {Number} padHeight padding below box..
34702      */   
34703     
34704     padHeight : 10, 
34705     
34706     /**
34707      * @cfg {Boolean} isAutoInitial defalut true
34708      */   
34709     
34710     isAutoInitial : true, 
34711     
34712     // private?
34713     gutter : 0,
34714     
34715     containerWidth: 0,
34716     initialColumnWidth : 0,
34717     currentSize : null,
34718     
34719     colYs : null, // array.
34720     maxY : 0,
34721     padWidth: 10,
34722     
34723     
34724     tag: 'div',
34725     cls: '',
34726     bricks: null, //CompositeElement
34727     cols : 0, // array?
34728     // element : null, // wrapped now this.el
34729     _isLayoutInited : null, 
34730     
34731     
34732     getAutoCreate : function(){
34733         
34734         var cfg = {
34735             tag: this.tag,
34736             cls: 'blog-masonary-wrapper ' + this.cls,
34737             cn : {
34738                 cls : 'mas-boxes masonary'
34739             }
34740         };
34741         
34742         return cfg;
34743     },
34744     
34745     getChildContainer: function( )
34746     {
34747         if (this.boxesEl) {
34748             return this.boxesEl;
34749         }
34750         
34751         this.boxesEl = this.el.select('.mas-boxes').first();
34752         
34753         return this.boxesEl;
34754     },
34755     
34756     
34757     initEvents : function()
34758     {
34759         var _this = this;
34760         
34761         if(this.isAutoInitial){
34762             Roo.log('hook children rendered');
34763             this.on('childrenrendered', function() {
34764                 Roo.log('children rendered');
34765                 _this.initial();
34766             } ,this);
34767         }
34768         
34769     },
34770     
34771     initial : function()
34772     {
34773         this.reloadItems();
34774
34775         this.currentSize = this.el.getBox(true);
34776
34777         /// was window resize... - let's see if this works..
34778         Roo.EventManager.onWindowResize(this.resize, this); 
34779
34780         if(!this.isAutoInitial){
34781             this.layout();
34782             return;
34783         }
34784         
34785         this.layout.defer(500,this);
34786     },
34787     
34788     reloadItems: function()
34789     {
34790         this.bricks = this.el.select('.masonry-brick', true);
34791         
34792         this.bricks.each(function(b) {
34793             //Roo.log(b.getSize());
34794             if (!b.attr('originalwidth')) {
34795                 b.attr('originalwidth',  b.getSize().width);
34796             }
34797             
34798         });
34799         
34800         Roo.log(this.bricks.elements.length);
34801     },
34802     
34803     resize : function()
34804     {
34805         Roo.log('resize');
34806         var cs = this.el.getBox(true);
34807         
34808         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34809             Roo.log("no change in with or X");
34810             return;
34811         }
34812         this.currentSize = cs;
34813         this.layout();
34814     },
34815     
34816     layout : function()
34817     {
34818          Roo.log('layout');
34819         this._resetLayout();
34820         //this._manageStamps();
34821       
34822         // don't animate first layout
34823         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34824         this.layoutItems( isInstant );
34825       
34826         // flag for initalized
34827         this._isLayoutInited = true;
34828     },
34829     
34830     layoutItems : function( isInstant )
34831     {
34832         //var items = this._getItemsForLayout( this.items );
34833         // original code supports filtering layout items.. we just ignore it..
34834         
34835         this._layoutItems( this.bricks , isInstant );
34836       
34837         this._postLayout();
34838     },
34839     _layoutItems : function ( items , isInstant)
34840     {
34841        //this.fireEvent( 'layout', this, items );
34842     
34843
34844         if ( !items || !items.elements.length ) {
34845           // no items, emit event with empty array
34846             return;
34847         }
34848
34849         var queue = [];
34850         items.each(function(item) {
34851             Roo.log("layout item");
34852             Roo.log(item);
34853             // get x/y object from method
34854             var position = this._getItemLayoutPosition( item );
34855             // enqueue
34856             position.item = item;
34857             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34858             queue.push( position );
34859         }, this);
34860       
34861         this._processLayoutQueue( queue );
34862     },
34863     /** Sets position of item in DOM
34864     * @param {Element} item
34865     * @param {Number} x - horizontal position
34866     * @param {Number} y - vertical position
34867     * @param {Boolean} isInstant - disables transitions
34868     */
34869     _processLayoutQueue : function( queue )
34870     {
34871         for ( var i=0, len = queue.length; i < len; i++ ) {
34872             var obj = queue[i];
34873             obj.item.position('absolute');
34874             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34875         }
34876     },
34877       
34878     
34879     /**
34880     * Any logic you want to do after each layout,
34881     * i.e. size the container
34882     */
34883     _postLayout : function()
34884     {
34885         this.resizeContainer();
34886     },
34887     
34888     resizeContainer : function()
34889     {
34890         if ( !this.isResizingContainer ) {
34891             return;
34892         }
34893         var size = this._getContainerSize();
34894         if ( size ) {
34895             this.el.setSize(size.width,size.height);
34896             this.boxesEl.setSize(size.width,size.height);
34897         }
34898     },
34899     
34900     
34901     
34902     _resetLayout : function()
34903     {
34904         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34905         this.colWidth = this.el.getWidth();
34906         //this.gutter = this.el.getWidth(); 
34907         
34908         this.measureColumns();
34909
34910         // reset column Y
34911         var i = this.cols;
34912         this.colYs = [];
34913         while (i--) {
34914             this.colYs.push( 0 );
34915         }
34916     
34917         this.maxY = 0;
34918     },
34919
34920     measureColumns : function()
34921     {
34922         this.getContainerWidth();
34923       // if columnWidth is 0, default to outerWidth of first item
34924         if ( !this.columnWidth ) {
34925             var firstItem = this.bricks.first();
34926             Roo.log(firstItem);
34927             this.columnWidth  = this.containerWidth;
34928             if (firstItem && firstItem.attr('originalwidth') ) {
34929                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34930             }
34931             // columnWidth fall back to item of first element
34932             Roo.log("set column width?");
34933                         this.initialColumnWidth = this.columnWidth  ;
34934
34935             // if first elem has no width, default to size of container
34936             
34937         }
34938         
34939         
34940         if (this.initialColumnWidth) {
34941             this.columnWidth = this.initialColumnWidth;
34942         }
34943         
34944         
34945             
34946         // column width is fixed at the top - however if container width get's smaller we should
34947         // reduce it...
34948         
34949         // this bit calcs how man columns..
34950             
34951         var columnWidth = this.columnWidth += this.gutter;
34952       
34953         // calculate columns
34954         var containerWidth = this.containerWidth + this.gutter;
34955         
34956         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34957         // fix rounding errors, typically with gutters
34958         var excess = columnWidth - containerWidth % columnWidth;
34959         
34960         
34961         // if overshoot is less than a pixel, round up, otherwise floor it
34962         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34963         cols = Math[ mathMethod ]( cols );
34964         this.cols = Math.max( cols, 1 );
34965         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34966         
34967          // padding positioning..
34968         var totalColWidth = this.cols * this.columnWidth;
34969         var padavail = this.containerWidth - totalColWidth;
34970         // so for 2 columns - we need 3 'pads'
34971         
34972         var padNeeded = (1+this.cols) * this.padWidth;
34973         
34974         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34975         
34976         this.columnWidth += padExtra
34977         //this.padWidth = Math.floor(padavail /  ( this.cols));
34978         
34979         // adjust colum width so that padding is fixed??
34980         
34981         // we have 3 columns ... total = width * 3
34982         // we have X left over... that should be used by 
34983         
34984         //if (this.expandC) {
34985             
34986         //}
34987         
34988         
34989         
34990     },
34991     
34992     getContainerWidth : function()
34993     {
34994        /* // container is parent if fit width
34995         var container = this.isFitWidth ? this.element.parentNode : this.element;
34996         // check that this.size and size are there
34997         // IE8 triggers resize on body size change, so they might not be
34998         
34999         var size = getSize( container );  //FIXME
35000         this.containerWidth = size && size.innerWidth; //FIXME
35001         */
35002          
35003         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35004         
35005     },
35006     
35007     _getItemLayoutPosition : function( item )  // what is item?
35008     {
35009         // we resize the item to our columnWidth..
35010       
35011         item.setWidth(this.columnWidth);
35012         item.autoBoxAdjust  = false;
35013         
35014         var sz = item.getSize();
35015  
35016         // how many columns does this brick span
35017         var remainder = this.containerWidth % this.columnWidth;
35018         
35019         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35020         // round if off by 1 pixel, otherwise use ceil
35021         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35022         colSpan = Math.min( colSpan, this.cols );
35023         
35024         // normally this should be '1' as we dont' currently allow multi width columns..
35025         
35026         var colGroup = this._getColGroup( colSpan );
35027         // get the minimum Y value from the columns
35028         var minimumY = Math.min.apply( Math, colGroup );
35029         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35030         
35031         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35032          
35033         // position the brick
35034         var position = {
35035             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35036             y: this.currentSize.y + minimumY + this.padHeight
35037         };
35038         
35039         Roo.log(position);
35040         // apply setHeight to necessary columns
35041         var setHeight = minimumY + sz.height + this.padHeight;
35042         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35043         
35044         var setSpan = this.cols + 1 - colGroup.length;
35045         for ( var i = 0; i < setSpan; i++ ) {
35046           this.colYs[ shortColIndex + i ] = setHeight ;
35047         }
35048       
35049         return position;
35050     },
35051     
35052     /**
35053      * @param {Number} colSpan - number of columns the element spans
35054      * @returns {Array} colGroup
35055      */
35056     _getColGroup : function( colSpan )
35057     {
35058         if ( colSpan < 2 ) {
35059           // if brick spans only one column, use all the column Ys
35060           return this.colYs;
35061         }
35062       
35063         var colGroup = [];
35064         // how many different places could this brick fit horizontally
35065         var groupCount = this.cols + 1 - colSpan;
35066         // for each group potential horizontal position
35067         for ( var i = 0; i < groupCount; i++ ) {
35068           // make an array of colY values for that one group
35069           var groupColYs = this.colYs.slice( i, i + colSpan );
35070           // and get the max value of the array
35071           colGroup[i] = Math.max.apply( Math, groupColYs );
35072         }
35073         return colGroup;
35074     },
35075     /*
35076     _manageStamp : function( stamp )
35077     {
35078         var stampSize =  stamp.getSize();
35079         var offset = stamp.getBox();
35080         // get the columns that this stamp affects
35081         var firstX = this.isOriginLeft ? offset.x : offset.right;
35082         var lastX = firstX + stampSize.width;
35083         var firstCol = Math.floor( firstX / this.columnWidth );
35084         firstCol = Math.max( 0, firstCol );
35085         
35086         var lastCol = Math.floor( lastX / this.columnWidth );
35087         // lastCol should not go over if multiple of columnWidth #425
35088         lastCol -= lastX % this.columnWidth ? 0 : 1;
35089         lastCol = Math.min( this.cols - 1, lastCol );
35090         
35091         // set colYs to bottom of the stamp
35092         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35093             stampSize.height;
35094             
35095         for ( var i = firstCol; i <= lastCol; i++ ) {
35096           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35097         }
35098     },
35099     */
35100     
35101     _getContainerSize : function()
35102     {
35103         this.maxY = Math.max.apply( Math, this.colYs );
35104         var size = {
35105             height: this.maxY
35106         };
35107       
35108         if ( this.isFitWidth ) {
35109             size.width = this._getContainerFitWidth();
35110         }
35111       
35112         return size;
35113     },
35114     
35115     _getContainerFitWidth : function()
35116     {
35117         var unusedCols = 0;
35118         // count unused columns
35119         var i = this.cols;
35120         while ( --i ) {
35121           if ( this.colYs[i] !== 0 ) {
35122             break;
35123           }
35124           unusedCols++;
35125         }
35126         // fit container to columns that have been used
35127         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35128     },
35129     
35130     needsResizeLayout : function()
35131     {
35132         var previousWidth = this.containerWidth;
35133         this.getContainerWidth();
35134         return previousWidth !== this.containerWidth;
35135     }
35136  
35137 });
35138
35139  
35140
35141  /*
35142  * - LGPL
35143  *
35144  * element
35145  * 
35146  */
35147
35148 /**
35149  * @class Roo.bootstrap.MasonryBrick
35150  * @extends Roo.bootstrap.Component
35151  * Bootstrap MasonryBrick class
35152  * 
35153  * @constructor
35154  * Create a new MasonryBrick
35155  * @param {Object} config The config object
35156  */
35157
35158 Roo.bootstrap.MasonryBrick = function(config){
35159     
35160     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35161     
35162     Roo.bootstrap.MasonryBrick.register(this);
35163     
35164     this.addEvents({
35165         // raw events
35166         /**
35167          * @event click
35168          * When a MasonryBrick is clcik
35169          * @param {Roo.bootstrap.MasonryBrick} this
35170          * @param {Roo.EventObject} e
35171          */
35172         "click" : true
35173     });
35174 };
35175
35176 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35177     
35178     /**
35179      * @cfg {String} title
35180      */   
35181     title : '',
35182     /**
35183      * @cfg {String} html
35184      */   
35185     html : '',
35186     /**
35187      * @cfg {String} bgimage
35188      */   
35189     bgimage : '',
35190     /**
35191      * @cfg {String} videourl
35192      */   
35193     videourl : '',
35194     /**
35195      * @cfg {String} cls
35196      */   
35197     cls : '',
35198     /**
35199      * @cfg {String} href
35200      */   
35201     href : '',
35202     /**
35203      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35204      */   
35205     size : 'xs',
35206     
35207     /**
35208      * @cfg {String} placetitle (center|bottom)
35209      */   
35210     placetitle : '',
35211     
35212     /**
35213      * @cfg {Boolean} isFitContainer defalut true
35214      */   
35215     isFitContainer : true, 
35216     
35217     /**
35218      * @cfg {Boolean} preventDefault defalut false
35219      */   
35220     preventDefault : false, 
35221     
35222     /**
35223      * @cfg {Boolean} inverse defalut false
35224      */   
35225     maskInverse : false, 
35226     
35227     getAutoCreate : function()
35228     {
35229         if(!this.isFitContainer){
35230             return this.getSplitAutoCreate();
35231         }
35232         
35233         var cls = 'masonry-brick masonry-brick-full';
35234         
35235         if(this.href.length){
35236             cls += ' masonry-brick-link';
35237         }
35238         
35239         if(this.bgimage.length){
35240             cls += ' masonry-brick-image';
35241         }
35242         
35243         if(this.maskInverse){
35244             cls += ' mask-inverse';
35245         }
35246         
35247         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35248             cls += ' enable-mask';
35249         }
35250         
35251         if(this.size){
35252             cls += ' masonry-' + this.size + '-brick';
35253         }
35254         
35255         if(this.placetitle.length){
35256             
35257             switch (this.placetitle) {
35258                 case 'center' :
35259                     cls += ' masonry-center-title';
35260                     break;
35261                 case 'bottom' :
35262                     cls += ' masonry-bottom-title';
35263                     break;
35264                 default:
35265                     break;
35266             }
35267             
35268         } else {
35269             if(!this.html.length && !this.bgimage.length){
35270                 cls += ' masonry-center-title';
35271             }
35272
35273             if(!this.html.length && this.bgimage.length){
35274                 cls += ' masonry-bottom-title';
35275             }
35276         }
35277         
35278         if(this.cls){
35279             cls += ' ' + this.cls;
35280         }
35281         
35282         var cfg = {
35283             tag: (this.href.length) ? 'a' : 'div',
35284             cls: cls,
35285             cn: [
35286                 {
35287                     tag: 'div',
35288                     cls: 'masonry-brick-mask'
35289                 },
35290                 {
35291                     tag: 'div',
35292                     cls: 'masonry-brick-paragraph',
35293                     cn: []
35294                 }
35295             ]
35296         };
35297         
35298         if(this.href.length){
35299             cfg.href = this.href;
35300         }
35301         
35302         var cn = cfg.cn[1].cn;
35303         
35304         if(this.title.length){
35305             cn.push({
35306                 tag: 'h4',
35307                 cls: 'masonry-brick-title',
35308                 html: this.title
35309             });
35310         }
35311         
35312         if(this.html.length){
35313             cn.push({
35314                 tag: 'p',
35315                 cls: 'masonry-brick-text',
35316                 html: this.html
35317             });
35318         }
35319         
35320         if (!this.title.length && !this.html.length) {
35321             cfg.cn[1].cls += ' hide';
35322         }
35323         
35324         if(this.bgimage.length){
35325             cfg.cn.push({
35326                 tag: 'img',
35327                 cls: 'masonry-brick-image-view',
35328                 src: this.bgimage
35329             });
35330         }
35331         
35332         if(this.videourl.length){
35333             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35334             // youtube support only?
35335             cfg.cn.push({
35336                 tag: 'iframe',
35337                 cls: 'masonry-brick-image-view',
35338                 src: vurl,
35339                 frameborder : 0,
35340                 allowfullscreen : true
35341             });
35342         }
35343         
35344         return cfg;
35345         
35346     },
35347     
35348     getSplitAutoCreate : function()
35349     {
35350         var cls = 'masonry-brick masonry-brick-split';
35351         
35352         if(this.href.length){
35353             cls += ' masonry-brick-link';
35354         }
35355         
35356         if(this.bgimage.length){
35357             cls += ' masonry-brick-image';
35358         }
35359         
35360         if(this.size){
35361             cls += ' masonry-' + this.size + '-brick';
35362         }
35363         
35364         switch (this.placetitle) {
35365             case 'center' :
35366                 cls += ' masonry-center-title';
35367                 break;
35368             case 'bottom' :
35369                 cls += ' masonry-bottom-title';
35370                 break;
35371             default:
35372                 if(!this.bgimage.length){
35373                     cls += ' masonry-center-title';
35374                 }
35375
35376                 if(this.bgimage.length){
35377                     cls += ' masonry-bottom-title';
35378                 }
35379                 break;
35380         }
35381         
35382         if(this.cls){
35383             cls += ' ' + this.cls;
35384         }
35385         
35386         var cfg = {
35387             tag: (this.href.length) ? 'a' : 'div',
35388             cls: cls,
35389             cn: [
35390                 {
35391                     tag: 'div',
35392                     cls: 'masonry-brick-split-head',
35393                     cn: [
35394                         {
35395                             tag: 'div',
35396                             cls: 'masonry-brick-paragraph',
35397                             cn: []
35398                         }
35399                     ]
35400                 },
35401                 {
35402                     tag: 'div',
35403                     cls: 'masonry-brick-split-body',
35404                     cn: []
35405                 }
35406             ]
35407         };
35408         
35409         if(this.href.length){
35410             cfg.href = this.href;
35411         }
35412         
35413         if(this.title.length){
35414             cfg.cn[0].cn[0].cn.push({
35415                 tag: 'h4',
35416                 cls: 'masonry-brick-title',
35417                 html: this.title
35418             });
35419         }
35420         
35421         if(this.html.length){
35422             cfg.cn[1].cn.push({
35423                 tag: 'p',
35424                 cls: 'masonry-brick-text',
35425                 html: this.html
35426             });
35427         }
35428
35429         if(this.bgimage.length){
35430             cfg.cn[0].cn.push({
35431                 tag: 'img',
35432                 cls: 'masonry-brick-image-view',
35433                 src: this.bgimage
35434             });
35435         }
35436         
35437         if(this.videourl.length){
35438             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35439             // youtube support only?
35440             cfg.cn[0].cn.cn.push({
35441                 tag: 'iframe',
35442                 cls: 'masonry-brick-image-view',
35443                 src: vurl,
35444                 frameborder : 0,
35445                 allowfullscreen : true
35446             });
35447         }
35448         
35449         return cfg;
35450     },
35451     
35452     initEvents: function() 
35453     {
35454         switch (this.size) {
35455             case 'xs' :
35456                 this.x = 1;
35457                 this.y = 1;
35458                 break;
35459             case 'sm' :
35460                 this.x = 2;
35461                 this.y = 2;
35462                 break;
35463             case 'md' :
35464             case 'md-left' :
35465             case 'md-right' :
35466                 this.x = 3;
35467                 this.y = 3;
35468                 break;
35469             case 'tall' :
35470                 this.x = 2;
35471                 this.y = 3;
35472                 break;
35473             case 'wide' :
35474                 this.x = 3;
35475                 this.y = 2;
35476                 break;
35477             case 'wide-thin' :
35478                 this.x = 3;
35479                 this.y = 1;
35480                 break;
35481                         
35482             default :
35483                 break;
35484         }
35485         
35486         if(Roo.isTouch){
35487             this.el.on('touchstart', this.onTouchStart, this);
35488             this.el.on('touchmove', this.onTouchMove, this);
35489             this.el.on('touchend', this.onTouchEnd, this);
35490             this.el.on('contextmenu', this.onContextMenu, this);
35491         } else {
35492             this.el.on('mouseenter'  ,this.enter, this);
35493             this.el.on('mouseleave', this.leave, this);
35494             this.el.on('click', this.onClick, this);
35495         }
35496         
35497         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35498             this.parent().bricks.push(this);   
35499         }
35500         
35501     },
35502     
35503     onClick: function(e, el)
35504     {
35505         var time = this.endTimer - this.startTimer;
35506         // Roo.log(e.preventDefault());
35507         if(Roo.isTouch){
35508             if(time > 1000){
35509                 e.preventDefault();
35510                 return;
35511             }
35512         }
35513         
35514         if(!this.preventDefault){
35515             return;
35516         }
35517         
35518         e.preventDefault();
35519         
35520         if (this.activeClass != '') {
35521             this.selectBrick();
35522         }
35523         
35524         this.fireEvent('click', this, e);
35525     },
35526     
35527     enter: function(e, el)
35528     {
35529         e.preventDefault();
35530         
35531         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35532             return;
35533         }
35534         
35535         if(this.bgimage.length && this.html.length){
35536             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35537         }
35538     },
35539     
35540     leave: function(e, el)
35541     {
35542         e.preventDefault();
35543         
35544         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35545             return;
35546         }
35547         
35548         if(this.bgimage.length && this.html.length){
35549             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35550         }
35551     },
35552     
35553     onTouchStart: function(e, el)
35554     {
35555 //        e.preventDefault();
35556         
35557         this.touchmoved = false;
35558         
35559         if(!this.isFitContainer){
35560             return;
35561         }
35562         
35563         if(!this.bgimage.length || !this.html.length){
35564             return;
35565         }
35566         
35567         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35568         
35569         this.timer = new Date().getTime();
35570         
35571     },
35572     
35573     onTouchMove: function(e, el)
35574     {
35575         this.touchmoved = true;
35576     },
35577     
35578     onContextMenu : function(e,el)
35579     {
35580         e.preventDefault();
35581         e.stopPropagation();
35582         return false;
35583     },
35584     
35585     onTouchEnd: function(e, el)
35586     {
35587 //        e.preventDefault();
35588         
35589         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35590         
35591             this.leave(e,el);
35592             
35593             return;
35594         }
35595         
35596         if(!this.bgimage.length || !this.html.length){
35597             
35598             if(this.href.length){
35599                 window.location.href = this.href;
35600             }
35601             
35602             return;
35603         }
35604         
35605         if(!this.isFitContainer){
35606             return;
35607         }
35608         
35609         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35610         
35611         window.location.href = this.href;
35612     },
35613     
35614     //selection on single brick only
35615     selectBrick : function() {
35616         
35617         if (!this.parentId) {
35618             return;
35619         }
35620         
35621         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35622         var index = m.selectedBrick.indexOf(this.id);
35623         
35624         if ( index > -1) {
35625             m.selectedBrick.splice(index,1);
35626             this.el.removeClass(this.activeClass);
35627             return;
35628         }
35629         
35630         for(var i = 0; i < m.selectedBrick.length; i++) {
35631             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35632             b.el.removeClass(b.activeClass);
35633         }
35634         
35635         m.selectedBrick = [];
35636         
35637         m.selectedBrick.push(this.id);
35638         this.el.addClass(this.activeClass);
35639         return;
35640     },
35641     
35642     isSelected : function(){
35643         return this.el.hasClass(this.activeClass);
35644         
35645     }
35646 });
35647
35648 Roo.apply(Roo.bootstrap.MasonryBrick, {
35649     
35650     //groups: {},
35651     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35652      /**
35653     * register a Masonry Brick
35654     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35655     */
35656     
35657     register : function(brick)
35658     {
35659         //this.groups[brick.id] = brick;
35660         this.groups.add(brick.id, brick);
35661     },
35662     /**
35663     * fetch a  masonry brick based on the masonry brick ID
35664     * @param {string} the masonry brick to add
35665     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35666     */
35667     
35668     get: function(brick_id) 
35669     {
35670         // if (typeof(this.groups[brick_id]) == 'undefined') {
35671         //     return false;
35672         // }
35673         // return this.groups[brick_id] ;
35674         
35675         if(this.groups.key(brick_id)) {
35676             return this.groups.key(brick_id);
35677         }
35678         
35679         return false;
35680     }
35681     
35682     
35683     
35684 });
35685
35686  /*
35687  * - LGPL
35688  *
35689  * element
35690  * 
35691  */
35692
35693 /**
35694  * @class Roo.bootstrap.Brick
35695  * @extends Roo.bootstrap.Component
35696  * Bootstrap Brick class
35697  * 
35698  * @constructor
35699  * Create a new Brick
35700  * @param {Object} config The config object
35701  */
35702
35703 Roo.bootstrap.Brick = function(config){
35704     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35705     
35706     this.addEvents({
35707         // raw events
35708         /**
35709          * @event click
35710          * When a Brick is click
35711          * @param {Roo.bootstrap.Brick} this
35712          * @param {Roo.EventObject} e
35713          */
35714         "click" : true
35715     });
35716 };
35717
35718 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35719     
35720     /**
35721      * @cfg {String} title
35722      */   
35723     title : '',
35724     /**
35725      * @cfg {String} html
35726      */   
35727     html : '',
35728     /**
35729      * @cfg {String} bgimage
35730      */   
35731     bgimage : '',
35732     /**
35733      * @cfg {String} cls
35734      */   
35735     cls : '',
35736     /**
35737      * @cfg {String} href
35738      */   
35739     href : '',
35740     /**
35741      * @cfg {String} video
35742      */   
35743     video : '',
35744     /**
35745      * @cfg {Boolean} square
35746      */   
35747     square : true,
35748     
35749     getAutoCreate : function()
35750     {
35751         var cls = 'roo-brick';
35752         
35753         if(this.href.length){
35754             cls += ' roo-brick-link';
35755         }
35756         
35757         if(this.bgimage.length){
35758             cls += ' roo-brick-image';
35759         }
35760         
35761         if(!this.html.length && !this.bgimage.length){
35762             cls += ' roo-brick-center-title';
35763         }
35764         
35765         if(!this.html.length && this.bgimage.length){
35766             cls += ' roo-brick-bottom-title';
35767         }
35768         
35769         if(this.cls){
35770             cls += ' ' + this.cls;
35771         }
35772         
35773         var cfg = {
35774             tag: (this.href.length) ? 'a' : 'div',
35775             cls: cls,
35776             cn: [
35777                 {
35778                     tag: 'div',
35779                     cls: 'roo-brick-paragraph',
35780                     cn: []
35781                 }
35782             ]
35783         };
35784         
35785         if(this.href.length){
35786             cfg.href = this.href;
35787         }
35788         
35789         var cn = cfg.cn[0].cn;
35790         
35791         if(this.title.length){
35792             cn.push({
35793                 tag: 'h4',
35794                 cls: 'roo-brick-title',
35795                 html: this.title
35796             });
35797         }
35798         
35799         if(this.html.length){
35800             cn.push({
35801                 tag: 'p',
35802                 cls: 'roo-brick-text',
35803                 html: this.html
35804             });
35805         } else {
35806             cn.cls += ' hide';
35807         }
35808         
35809         if(this.bgimage.length){
35810             cfg.cn.push({
35811                 tag: 'img',
35812                 cls: 'roo-brick-image-view',
35813                 src: this.bgimage
35814             });
35815         }
35816         
35817         return cfg;
35818     },
35819     
35820     initEvents: function() 
35821     {
35822         if(this.title.length || this.html.length){
35823             this.el.on('mouseenter'  ,this.enter, this);
35824             this.el.on('mouseleave', this.leave, this);
35825         }
35826         
35827         Roo.EventManager.onWindowResize(this.resize, this); 
35828         
35829         if(this.bgimage.length){
35830             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35831             this.imageEl.on('load', this.onImageLoad, this);
35832             return;
35833         }
35834         
35835         this.resize();
35836     },
35837     
35838     onImageLoad : function()
35839     {
35840         this.resize();
35841     },
35842     
35843     resize : function()
35844     {
35845         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35846         
35847         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35848         
35849         if(this.bgimage.length){
35850             var image = this.el.select('.roo-brick-image-view', true).first();
35851             
35852             image.setWidth(paragraph.getWidth());
35853             
35854             if(this.square){
35855                 image.setHeight(paragraph.getWidth());
35856             }
35857             
35858             this.el.setHeight(image.getHeight());
35859             paragraph.setHeight(image.getHeight());
35860             
35861         }
35862         
35863     },
35864     
35865     enter: function(e, el)
35866     {
35867         e.preventDefault();
35868         
35869         if(this.bgimage.length){
35870             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35871             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35872         }
35873     },
35874     
35875     leave: function(e, el)
35876     {
35877         e.preventDefault();
35878         
35879         if(this.bgimage.length){
35880             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35881             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35882         }
35883     }
35884     
35885 });
35886
35887  
35888
35889  /*
35890  * - LGPL
35891  *
35892  * Number field 
35893  */
35894
35895 /**
35896  * @class Roo.bootstrap.NumberField
35897  * @extends Roo.bootstrap.Input
35898  * Bootstrap NumberField class
35899  * 
35900  * 
35901  * 
35902  * 
35903  * @constructor
35904  * Create a new NumberField
35905  * @param {Object} config The config object
35906  */
35907
35908 Roo.bootstrap.NumberField = function(config){
35909     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35910 };
35911
35912 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35913     
35914     /**
35915      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35916      */
35917     allowDecimals : true,
35918     /**
35919      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35920      */
35921     decimalSeparator : ".",
35922     /**
35923      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35924      */
35925     decimalPrecision : 2,
35926     /**
35927      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35928      */
35929     allowNegative : true,
35930     
35931     /**
35932      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35933      */
35934     allowZero: true,
35935     /**
35936      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35937      */
35938     minValue : Number.NEGATIVE_INFINITY,
35939     /**
35940      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35941      */
35942     maxValue : Number.MAX_VALUE,
35943     /**
35944      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35945      */
35946     minText : "The minimum value for this field is {0}",
35947     /**
35948      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35949      */
35950     maxText : "The maximum value for this field is {0}",
35951     /**
35952      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35953      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35954      */
35955     nanText : "{0} is not a valid number",
35956     /**
35957      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35958      */
35959     thousandsDelimiter : false,
35960     /**
35961      * @cfg {String} valueAlign alignment of value
35962      */
35963     valueAlign : "left",
35964
35965     getAutoCreate : function()
35966     {
35967         var hiddenInput = {
35968             tag: 'input',
35969             type: 'hidden',
35970             id: Roo.id(),
35971             cls: 'hidden-number-input'
35972         };
35973         
35974         if (this.name) {
35975             hiddenInput.name = this.name;
35976         }
35977         
35978         this.name = '';
35979         
35980         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35981         
35982         this.name = hiddenInput.name;
35983         
35984         if(cfg.cn.length > 0) {
35985             cfg.cn.push(hiddenInput);
35986         }
35987         
35988         return cfg;
35989     },
35990
35991     // private
35992     initEvents : function()
35993     {   
35994         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35995         
35996         var allowed = "0123456789";
35997         
35998         if(this.allowDecimals){
35999             allowed += this.decimalSeparator;
36000         }
36001         
36002         if(this.allowNegative){
36003             allowed += "-";
36004         }
36005         
36006         if(this.thousandsDelimiter) {
36007             allowed += ",";
36008         }
36009         
36010         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36011         
36012         var keyPress = function(e){
36013             
36014             var k = e.getKey();
36015             
36016             var c = e.getCharCode();
36017             
36018             if(
36019                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36020                     allowed.indexOf(String.fromCharCode(c)) === -1
36021             ){
36022                 e.stopEvent();
36023                 return;
36024             }
36025             
36026             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36027                 return;
36028             }
36029             
36030             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36031                 e.stopEvent();
36032             }
36033         };
36034         
36035         this.el.on("keypress", keyPress, this);
36036     },
36037     
36038     validateValue : function(value)
36039     {
36040         
36041         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36042             return false;
36043         }
36044         
36045         var num = this.parseValue(value);
36046         
36047         if(isNaN(num)){
36048             this.markInvalid(String.format(this.nanText, value));
36049             return false;
36050         }
36051         
36052         if(num < this.minValue){
36053             this.markInvalid(String.format(this.minText, this.minValue));
36054             return false;
36055         }
36056         
36057         if(num > this.maxValue){
36058             this.markInvalid(String.format(this.maxText, this.maxValue));
36059             return false;
36060         }
36061         
36062         return true;
36063     },
36064
36065     getValue : function()
36066     {
36067         var v = this.hiddenEl().getValue();
36068         
36069         return this.fixPrecision(this.parseValue(v));
36070     },
36071
36072     parseValue : function(value)
36073     {
36074         if(this.thousandsDelimiter) {
36075             value += "";
36076             r = new RegExp(",", "g");
36077             value = value.replace(r, "");
36078         }
36079         
36080         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36081         return isNaN(value) ? '' : value;
36082     },
36083
36084     fixPrecision : function(value)
36085     {
36086         if(this.thousandsDelimiter) {
36087             value += "";
36088             r = new RegExp(",", "g");
36089             value = value.replace(r, "");
36090         }
36091         
36092         var nan = isNaN(value);
36093         
36094         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36095             return nan ? '' : value;
36096         }
36097         return parseFloat(value).toFixed(this.decimalPrecision);
36098     },
36099
36100     setValue : function(v)
36101     {
36102         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36103         
36104         this.value = v;
36105         
36106         if(this.rendered){
36107             
36108             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36109             
36110             this.inputEl().dom.value = (v == '') ? '' :
36111                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36112             
36113             if(!this.allowZero && v === '0') {
36114                 this.hiddenEl().dom.value = '';
36115                 this.inputEl().dom.value = '';
36116             }
36117             
36118             this.validate();
36119         }
36120     },
36121
36122     decimalPrecisionFcn : function(v)
36123     {
36124         return Math.floor(v);
36125     },
36126
36127     beforeBlur : function()
36128     {
36129         var v = this.parseValue(this.getRawValue());
36130         
36131         if(v || v === 0 || v === ''){
36132             this.setValue(v);
36133         }
36134     },
36135     
36136     hiddenEl : function()
36137     {
36138         return this.el.select('input.hidden-number-input',true).first();
36139     }
36140     
36141 });
36142
36143  
36144
36145 /*
36146 * Licence: LGPL
36147 */
36148
36149 /**
36150  * @class Roo.bootstrap.DocumentSlider
36151  * @extends Roo.bootstrap.Component
36152  * Bootstrap DocumentSlider class
36153  * 
36154  * @constructor
36155  * Create a new DocumentViewer
36156  * @param {Object} config The config object
36157  */
36158
36159 Roo.bootstrap.DocumentSlider = function(config){
36160     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36161     
36162     this.files = [];
36163     
36164     this.addEvents({
36165         /**
36166          * @event initial
36167          * Fire after initEvent
36168          * @param {Roo.bootstrap.DocumentSlider} this
36169          */
36170         "initial" : true,
36171         /**
36172          * @event update
36173          * Fire after update
36174          * @param {Roo.bootstrap.DocumentSlider} this
36175          */
36176         "update" : true,
36177         /**
36178          * @event click
36179          * Fire after click
36180          * @param {Roo.bootstrap.DocumentSlider} this
36181          */
36182         "click" : true
36183     });
36184 };
36185
36186 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36187     
36188     files : false,
36189     
36190     indicator : 0,
36191     
36192     getAutoCreate : function()
36193     {
36194         var cfg = {
36195             tag : 'div',
36196             cls : 'roo-document-slider',
36197             cn : [
36198                 {
36199                     tag : 'div',
36200                     cls : 'roo-document-slider-header',
36201                     cn : [
36202                         {
36203                             tag : 'div',
36204                             cls : 'roo-document-slider-header-title'
36205                         }
36206                     ]
36207                 },
36208                 {
36209                     tag : 'div',
36210                     cls : 'roo-document-slider-body',
36211                     cn : [
36212                         {
36213                             tag : 'div',
36214                             cls : 'roo-document-slider-prev',
36215                             cn : [
36216                                 {
36217                                     tag : 'i',
36218                                     cls : 'fa fa-chevron-left'
36219                                 }
36220                             ]
36221                         },
36222                         {
36223                             tag : 'div',
36224                             cls : 'roo-document-slider-thumb',
36225                             cn : [
36226                                 {
36227                                     tag : 'img',
36228                                     cls : 'roo-document-slider-image'
36229                                 }
36230                             ]
36231                         },
36232                         {
36233                             tag : 'div',
36234                             cls : 'roo-document-slider-next',
36235                             cn : [
36236                                 {
36237                                     tag : 'i',
36238                                     cls : 'fa fa-chevron-right'
36239                                 }
36240                             ]
36241                         }
36242                     ]
36243                 }
36244             ]
36245         };
36246         
36247         return cfg;
36248     },
36249     
36250     initEvents : function()
36251     {
36252         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36253         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36254         
36255         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36256         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36257         
36258         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36259         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36260         
36261         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36262         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36263         
36264         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36265         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36266         
36267         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36268         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36269         
36270         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36271         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36272         
36273         this.thumbEl.on('click', this.onClick, this);
36274         
36275         this.prevIndicator.on('click', this.prev, this);
36276         
36277         this.nextIndicator.on('click', this.next, this);
36278         
36279     },
36280     
36281     initial : function()
36282     {
36283         if(this.files.length){
36284             this.indicator = 1;
36285             this.update()
36286         }
36287         
36288         this.fireEvent('initial', this);
36289     },
36290     
36291     update : function()
36292     {
36293         this.imageEl.attr('src', this.files[this.indicator - 1]);
36294         
36295         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36296         
36297         this.prevIndicator.show();
36298         
36299         if(this.indicator == 1){
36300             this.prevIndicator.hide();
36301         }
36302         
36303         this.nextIndicator.show();
36304         
36305         if(this.indicator == this.files.length){
36306             this.nextIndicator.hide();
36307         }
36308         
36309         this.thumbEl.scrollTo('top');
36310         
36311         this.fireEvent('update', this);
36312     },
36313     
36314     onClick : function(e)
36315     {
36316         e.preventDefault();
36317         
36318         this.fireEvent('click', this);
36319     },
36320     
36321     prev : function(e)
36322     {
36323         e.preventDefault();
36324         
36325         this.indicator = Math.max(1, this.indicator - 1);
36326         
36327         this.update();
36328     },
36329     
36330     next : function(e)
36331     {
36332         e.preventDefault();
36333         
36334         this.indicator = Math.min(this.files.length, this.indicator + 1);
36335         
36336         this.update();
36337     }
36338 });
36339 /*
36340  * - LGPL
36341  *
36342  * RadioSet
36343  *
36344  *
36345  */
36346
36347 /**
36348  * @class Roo.bootstrap.RadioSet
36349  * @extends Roo.bootstrap.Input
36350  * Bootstrap RadioSet class
36351  * @cfg {String} indicatorpos (left|right) default left
36352  * @cfg {Boolean} inline (true|false) inline the element (default true)
36353  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36354  * @constructor
36355  * Create a new RadioSet
36356  * @param {Object} config The config object
36357  */
36358
36359 Roo.bootstrap.RadioSet = function(config){
36360     
36361     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36362     
36363     this.radioes = [];
36364     
36365     Roo.bootstrap.RadioSet.register(this);
36366     
36367     this.addEvents({
36368         /**
36369         * @event check
36370         * Fires when the element is checked or unchecked.
36371         * @param {Roo.bootstrap.RadioSet} this This radio
36372         * @param {Roo.bootstrap.Radio} item The checked item
36373         */
36374        check : true,
36375        /**
36376         * @event click
36377         * Fires when the element is click.
36378         * @param {Roo.bootstrap.RadioSet} this This radio set
36379         * @param {Roo.bootstrap.Radio} item The checked item
36380         * @param {Roo.EventObject} e The event object
36381         */
36382        click : true
36383     });
36384     
36385 };
36386
36387 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36388
36389     radioes : false,
36390     
36391     inline : true,
36392     
36393     weight : '',
36394     
36395     indicatorpos : 'left',
36396     
36397     getAutoCreate : function()
36398     {
36399         var label = {
36400             tag : 'label',
36401             cls : 'roo-radio-set-label',
36402             cn : [
36403                 {
36404                     tag : 'span',
36405                     html : this.fieldLabel
36406                 }
36407             ]
36408         };
36409         if (Roo.bootstrap.version == 3) {
36410             
36411             
36412             if(this.indicatorpos == 'left'){
36413                 label.cn.unshift({
36414                     tag : 'i',
36415                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36416                     tooltip : 'This field is required'
36417                 });
36418             } else {
36419                 label.cn.push({
36420                     tag : 'i',
36421                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36422                     tooltip : 'This field is required'
36423                 });
36424             }
36425         }
36426         var items = {
36427             tag : 'div',
36428             cls : 'roo-radio-set-items'
36429         };
36430         
36431         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36432         
36433         if (align === 'left' && this.fieldLabel.length) {
36434             
36435             items = {
36436                 cls : "roo-radio-set-right", 
36437                 cn: [
36438                     items
36439                 ]
36440             };
36441             
36442             if(this.labelWidth > 12){
36443                 label.style = "width: " + this.labelWidth + 'px';
36444             }
36445             
36446             if(this.labelWidth < 13 && this.labelmd == 0){
36447                 this.labelmd = this.labelWidth;
36448             }
36449             
36450             if(this.labellg > 0){
36451                 label.cls += ' col-lg-' + this.labellg;
36452                 items.cls += ' col-lg-' + (12 - this.labellg);
36453             }
36454             
36455             if(this.labelmd > 0){
36456                 label.cls += ' col-md-' + this.labelmd;
36457                 items.cls += ' col-md-' + (12 - this.labelmd);
36458             }
36459             
36460             if(this.labelsm > 0){
36461                 label.cls += ' col-sm-' + this.labelsm;
36462                 items.cls += ' col-sm-' + (12 - this.labelsm);
36463             }
36464             
36465             if(this.labelxs > 0){
36466                 label.cls += ' col-xs-' + this.labelxs;
36467                 items.cls += ' col-xs-' + (12 - this.labelxs);
36468             }
36469         }
36470         
36471         var cfg = {
36472             tag : 'div',
36473             cls : 'roo-radio-set',
36474             cn : [
36475                 {
36476                     tag : 'input',
36477                     cls : 'roo-radio-set-input',
36478                     type : 'hidden',
36479                     name : this.name,
36480                     value : this.value ? this.value :  ''
36481                 },
36482                 label,
36483                 items
36484             ]
36485         };
36486         
36487         if(this.weight.length){
36488             cfg.cls += ' roo-radio-' + this.weight;
36489         }
36490         
36491         if(this.inline) {
36492             cfg.cls += ' roo-radio-set-inline';
36493         }
36494         
36495         var settings=this;
36496         ['xs','sm','md','lg'].map(function(size){
36497             if (settings[size]) {
36498                 cfg.cls += ' col-' + size + '-' + settings[size];
36499             }
36500         });
36501         
36502         return cfg;
36503         
36504     },
36505
36506     initEvents : function()
36507     {
36508         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36509         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36510         
36511         if(!this.fieldLabel.length){
36512             this.labelEl.hide();
36513         }
36514         
36515         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36516         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36517         
36518         this.indicator = this.indicatorEl();
36519         
36520         if(this.indicator){
36521             this.indicator.addClass('invisible');
36522         }
36523         
36524         this.originalValue = this.getValue();
36525         
36526     },
36527     
36528     inputEl: function ()
36529     {
36530         return this.el.select('.roo-radio-set-input', true).first();
36531     },
36532     
36533     getChildContainer : function()
36534     {
36535         return this.itemsEl;
36536     },
36537     
36538     register : function(item)
36539     {
36540         this.radioes.push(item);
36541         
36542     },
36543     
36544     validate : function()
36545     {   
36546         if(this.getVisibilityEl().hasClass('hidden')){
36547             return true;
36548         }
36549         
36550         var valid = false;
36551         
36552         Roo.each(this.radioes, function(i){
36553             if(!i.checked){
36554                 return;
36555             }
36556             
36557             valid = true;
36558             return false;
36559         });
36560         
36561         if(this.allowBlank) {
36562             return true;
36563         }
36564         
36565         if(this.disabled || valid){
36566             this.markValid();
36567             return true;
36568         }
36569         
36570         this.markInvalid();
36571         return false;
36572         
36573     },
36574     
36575     markValid : function()
36576     {
36577         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36578             this.indicatorEl().removeClass('visible');
36579             this.indicatorEl().addClass('invisible');
36580         }
36581         
36582         
36583         if (Roo.bootstrap.version == 3) {
36584             this.el.removeClass([this.invalidClass, this.validClass]);
36585             this.el.addClass(this.validClass);
36586         } else {
36587             this.el.removeClass(['is-invalid','is-valid']);
36588             this.el.addClass(['is-valid']);
36589         }
36590         this.fireEvent('valid', this);
36591     },
36592     
36593     markInvalid : function(msg)
36594     {
36595         if(this.allowBlank || this.disabled){
36596             return;
36597         }
36598         
36599         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36600             this.indicatorEl().removeClass('invisible');
36601             this.indicatorEl().addClass('visible');
36602         }
36603         if (Roo.bootstrap.version == 3) {
36604             this.el.removeClass([this.invalidClass, this.validClass]);
36605             this.el.addClass(this.invalidClass);
36606         } else {
36607             this.el.removeClass(['is-invalid','is-valid']);
36608             this.el.addClass(['is-invalid']);
36609         }
36610         
36611         this.fireEvent('invalid', this, msg);
36612         
36613     },
36614     
36615     setValue : function(v, suppressEvent)
36616     {   
36617         if(this.value === v){
36618             return;
36619         }
36620         
36621         this.value = v;
36622         
36623         if(this.rendered){
36624             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36625         }
36626         
36627         Roo.each(this.radioes, function(i){
36628             i.checked = false;
36629             i.el.removeClass('checked');
36630         });
36631         
36632         Roo.each(this.radioes, function(i){
36633             
36634             if(i.value === v || i.value.toString() === v.toString()){
36635                 i.checked = true;
36636                 i.el.addClass('checked');
36637                 
36638                 if(suppressEvent !== true){
36639                     this.fireEvent('check', this, i);
36640                 }
36641                 
36642                 return false;
36643             }
36644             
36645         }, this);
36646         
36647         this.validate();
36648     },
36649     
36650     clearInvalid : function(){
36651         
36652         if(!this.el || this.preventMark){
36653             return;
36654         }
36655         
36656         this.el.removeClass([this.invalidClass]);
36657         
36658         this.fireEvent('valid', this);
36659     }
36660     
36661 });
36662
36663 Roo.apply(Roo.bootstrap.RadioSet, {
36664     
36665     groups: {},
36666     
36667     register : function(set)
36668     {
36669         this.groups[set.name] = set;
36670     },
36671     
36672     get: function(name) 
36673     {
36674         if (typeof(this.groups[name]) == 'undefined') {
36675             return false;
36676         }
36677         
36678         return this.groups[name] ;
36679     }
36680     
36681 });
36682 /*
36683  * Based on:
36684  * Ext JS Library 1.1.1
36685  * Copyright(c) 2006-2007, Ext JS, LLC.
36686  *
36687  * Originally Released Under LGPL - original licence link has changed is not relivant.
36688  *
36689  * Fork - LGPL
36690  * <script type="text/javascript">
36691  */
36692
36693
36694 /**
36695  * @class Roo.bootstrap.SplitBar
36696  * @extends Roo.util.Observable
36697  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36698  * <br><br>
36699  * Usage:
36700  * <pre><code>
36701 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36702                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36703 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36704 split.minSize = 100;
36705 split.maxSize = 600;
36706 split.animate = true;
36707 split.on('moved', splitterMoved);
36708 </code></pre>
36709  * @constructor
36710  * Create a new SplitBar
36711  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36712  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36713  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36714  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36715                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36716                         position of the SplitBar).
36717  */
36718 Roo.bootstrap.SplitBar = function(cfg){
36719     
36720     /** @private */
36721     
36722     //{
36723     //  dragElement : elm
36724     //  resizingElement: el,
36725         // optional..
36726     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36727     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36728         // existingProxy ???
36729     //}
36730     
36731     this.el = Roo.get(cfg.dragElement, true);
36732     this.el.dom.unselectable = "on";
36733     /** @private */
36734     this.resizingEl = Roo.get(cfg.resizingElement, true);
36735
36736     /**
36737      * @private
36738      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36739      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36740      * @type Number
36741      */
36742     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36743     
36744     /**
36745      * The minimum size of the resizing element. (Defaults to 0)
36746      * @type Number
36747      */
36748     this.minSize = 0;
36749     
36750     /**
36751      * The maximum size of the resizing element. (Defaults to 2000)
36752      * @type Number
36753      */
36754     this.maxSize = 2000;
36755     
36756     /**
36757      * Whether to animate the transition to the new size
36758      * @type Boolean
36759      */
36760     this.animate = false;
36761     
36762     /**
36763      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36764      * @type Boolean
36765      */
36766     this.useShim = false;
36767     
36768     /** @private */
36769     this.shim = null;
36770     
36771     if(!cfg.existingProxy){
36772         /** @private */
36773         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36774     }else{
36775         this.proxy = Roo.get(cfg.existingProxy).dom;
36776     }
36777     /** @private */
36778     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36779     
36780     /** @private */
36781     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36782     
36783     /** @private */
36784     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36785     
36786     /** @private */
36787     this.dragSpecs = {};
36788     
36789     /**
36790      * @private The adapter to use to positon and resize elements
36791      */
36792     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36793     this.adapter.init(this);
36794     
36795     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36796         /** @private */
36797         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36798         this.el.addClass("roo-splitbar-h");
36799     }else{
36800         /** @private */
36801         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36802         this.el.addClass("roo-splitbar-v");
36803     }
36804     
36805     this.addEvents({
36806         /**
36807          * @event resize
36808          * Fires when the splitter is moved (alias for {@link #event-moved})
36809          * @param {Roo.bootstrap.SplitBar} this
36810          * @param {Number} newSize the new width or height
36811          */
36812         "resize" : true,
36813         /**
36814          * @event moved
36815          * Fires when the splitter is moved
36816          * @param {Roo.bootstrap.SplitBar} this
36817          * @param {Number} newSize the new width or height
36818          */
36819         "moved" : true,
36820         /**
36821          * @event beforeresize
36822          * Fires before the splitter is dragged
36823          * @param {Roo.bootstrap.SplitBar} this
36824          */
36825         "beforeresize" : true,
36826
36827         "beforeapply" : true
36828     });
36829
36830     Roo.util.Observable.call(this);
36831 };
36832
36833 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36834     onStartProxyDrag : function(x, y){
36835         this.fireEvent("beforeresize", this);
36836         if(!this.overlay){
36837             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36838             o.unselectable();
36839             o.enableDisplayMode("block");
36840             // all splitbars share the same overlay
36841             Roo.bootstrap.SplitBar.prototype.overlay = o;
36842         }
36843         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36844         this.overlay.show();
36845         Roo.get(this.proxy).setDisplayed("block");
36846         var size = this.adapter.getElementSize(this);
36847         this.activeMinSize = this.getMinimumSize();;
36848         this.activeMaxSize = this.getMaximumSize();;
36849         var c1 = size - this.activeMinSize;
36850         var c2 = Math.max(this.activeMaxSize - size, 0);
36851         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36852             this.dd.resetConstraints();
36853             this.dd.setXConstraint(
36854                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36855                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36856             );
36857             this.dd.setYConstraint(0, 0);
36858         }else{
36859             this.dd.resetConstraints();
36860             this.dd.setXConstraint(0, 0);
36861             this.dd.setYConstraint(
36862                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36863                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36864             );
36865          }
36866         this.dragSpecs.startSize = size;
36867         this.dragSpecs.startPoint = [x, y];
36868         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36869     },
36870     
36871     /** 
36872      * @private Called after the drag operation by the DDProxy
36873      */
36874     onEndProxyDrag : function(e){
36875         Roo.get(this.proxy).setDisplayed(false);
36876         var endPoint = Roo.lib.Event.getXY(e);
36877         if(this.overlay){
36878             this.overlay.hide();
36879         }
36880         var newSize;
36881         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36882             newSize = this.dragSpecs.startSize + 
36883                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36884                     endPoint[0] - this.dragSpecs.startPoint[0] :
36885                     this.dragSpecs.startPoint[0] - endPoint[0]
36886                 );
36887         }else{
36888             newSize = this.dragSpecs.startSize + 
36889                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36890                     endPoint[1] - this.dragSpecs.startPoint[1] :
36891                     this.dragSpecs.startPoint[1] - endPoint[1]
36892                 );
36893         }
36894         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36895         if(newSize != this.dragSpecs.startSize){
36896             if(this.fireEvent('beforeapply', this, newSize) !== false){
36897                 this.adapter.setElementSize(this, newSize);
36898                 this.fireEvent("moved", this, newSize);
36899                 this.fireEvent("resize", this, newSize);
36900             }
36901         }
36902     },
36903     
36904     /**
36905      * Get the adapter this SplitBar uses
36906      * @return The adapter object
36907      */
36908     getAdapter : function(){
36909         return this.adapter;
36910     },
36911     
36912     /**
36913      * Set the adapter this SplitBar uses
36914      * @param {Object} adapter A SplitBar adapter object
36915      */
36916     setAdapter : function(adapter){
36917         this.adapter = adapter;
36918         this.adapter.init(this);
36919     },
36920     
36921     /**
36922      * Gets the minimum size for the resizing element
36923      * @return {Number} The minimum size
36924      */
36925     getMinimumSize : function(){
36926         return this.minSize;
36927     },
36928     
36929     /**
36930      * Sets the minimum size for the resizing element
36931      * @param {Number} minSize The minimum size
36932      */
36933     setMinimumSize : function(minSize){
36934         this.minSize = minSize;
36935     },
36936     
36937     /**
36938      * Gets the maximum size for the resizing element
36939      * @return {Number} The maximum size
36940      */
36941     getMaximumSize : function(){
36942         return this.maxSize;
36943     },
36944     
36945     /**
36946      * Sets the maximum size for the resizing element
36947      * @param {Number} maxSize The maximum size
36948      */
36949     setMaximumSize : function(maxSize){
36950         this.maxSize = maxSize;
36951     },
36952     
36953     /**
36954      * Sets the initialize size for the resizing element
36955      * @param {Number} size The initial size
36956      */
36957     setCurrentSize : function(size){
36958         var oldAnimate = this.animate;
36959         this.animate = false;
36960         this.adapter.setElementSize(this, size);
36961         this.animate = oldAnimate;
36962     },
36963     
36964     /**
36965      * Destroy this splitbar. 
36966      * @param {Boolean} removeEl True to remove the element
36967      */
36968     destroy : function(removeEl){
36969         if(this.shim){
36970             this.shim.remove();
36971         }
36972         this.dd.unreg();
36973         this.proxy.parentNode.removeChild(this.proxy);
36974         if(removeEl){
36975             this.el.remove();
36976         }
36977     }
36978 });
36979
36980 /**
36981  * @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.
36982  */
36983 Roo.bootstrap.SplitBar.createProxy = function(dir){
36984     var proxy = new Roo.Element(document.createElement("div"));
36985     proxy.unselectable();
36986     var cls = 'roo-splitbar-proxy';
36987     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36988     document.body.appendChild(proxy.dom);
36989     return proxy.dom;
36990 };
36991
36992 /** 
36993  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36994  * Default Adapter. It assumes the splitter and resizing element are not positioned
36995  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36996  */
36997 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36998 };
36999
37000 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37001     // do nothing for now
37002     init : function(s){
37003     
37004     },
37005     /**
37006      * Called before drag operations to get the current size of the resizing element. 
37007      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37008      */
37009      getElementSize : function(s){
37010         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37011             return s.resizingEl.getWidth();
37012         }else{
37013             return s.resizingEl.getHeight();
37014         }
37015     },
37016     
37017     /**
37018      * Called after drag operations to set the size of the resizing element.
37019      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37020      * @param {Number} newSize The new size to set
37021      * @param {Function} onComplete A function to be invoked when resizing is complete
37022      */
37023     setElementSize : function(s, newSize, onComplete){
37024         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37025             if(!s.animate){
37026                 s.resizingEl.setWidth(newSize);
37027                 if(onComplete){
37028                     onComplete(s, newSize);
37029                 }
37030             }else{
37031                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37032             }
37033         }else{
37034             
37035             if(!s.animate){
37036                 s.resizingEl.setHeight(newSize);
37037                 if(onComplete){
37038                     onComplete(s, newSize);
37039                 }
37040             }else{
37041                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37042             }
37043         }
37044     }
37045 };
37046
37047 /** 
37048  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37049  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37050  * Adapter that  moves the splitter element to align with the resized sizing element. 
37051  * Used with an absolute positioned SplitBar.
37052  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37053  * document.body, make sure you assign an id to the body element.
37054  */
37055 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37056     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37057     this.container = Roo.get(container);
37058 };
37059
37060 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37061     init : function(s){
37062         this.basic.init(s);
37063     },
37064     
37065     getElementSize : function(s){
37066         return this.basic.getElementSize(s);
37067     },
37068     
37069     setElementSize : function(s, newSize, onComplete){
37070         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37071     },
37072     
37073     moveSplitter : function(s){
37074         var yes = Roo.bootstrap.SplitBar;
37075         switch(s.placement){
37076             case yes.LEFT:
37077                 s.el.setX(s.resizingEl.getRight());
37078                 break;
37079             case yes.RIGHT:
37080                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37081                 break;
37082             case yes.TOP:
37083                 s.el.setY(s.resizingEl.getBottom());
37084                 break;
37085             case yes.BOTTOM:
37086                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37087                 break;
37088         }
37089     }
37090 };
37091
37092 /**
37093  * Orientation constant - Create a vertical SplitBar
37094  * @static
37095  * @type Number
37096  */
37097 Roo.bootstrap.SplitBar.VERTICAL = 1;
37098
37099 /**
37100  * Orientation constant - Create a horizontal SplitBar
37101  * @static
37102  * @type Number
37103  */
37104 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37105
37106 /**
37107  * Placement constant - The resizing element is to the left of the splitter element
37108  * @static
37109  * @type Number
37110  */
37111 Roo.bootstrap.SplitBar.LEFT = 1;
37112
37113 /**
37114  * Placement constant - The resizing element is to the right of the splitter element
37115  * @static
37116  * @type Number
37117  */
37118 Roo.bootstrap.SplitBar.RIGHT = 2;
37119
37120 /**
37121  * Placement constant - The resizing element is positioned above the splitter element
37122  * @static
37123  * @type Number
37124  */
37125 Roo.bootstrap.SplitBar.TOP = 3;
37126
37127 /**
37128  * Placement constant - The resizing element is positioned under splitter element
37129  * @static
37130  * @type Number
37131  */
37132 Roo.bootstrap.SplitBar.BOTTOM = 4;
37133 Roo.namespace("Roo.bootstrap.layout");/*
37134  * Based on:
37135  * Ext JS Library 1.1.1
37136  * Copyright(c) 2006-2007, Ext JS, LLC.
37137  *
37138  * Originally Released Under LGPL - original licence link has changed is not relivant.
37139  *
37140  * Fork - LGPL
37141  * <script type="text/javascript">
37142  */
37143
37144 /**
37145  * @class Roo.bootstrap.layout.Manager
37146  * @extends Roo.bootstrap.Component
37147  * Base class for layout managers.
37148  */
37149 Roo.bootstrap.layout.Manager = function(config)
37150 {
37151     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37152
37153
37154
37155
37156
37157     /** false to disable window resize monitoring @type Boolean */
37158     this.monitorWindowResize = true;
37159     this.regions = {};
37160     this.addEvents({
37161         /**
37162          * @event layout
37163          * Fires when a layout is performed.
37164          * @param {Roo.LayoutManager} this
37165          */
37166         "layout" : true,
37167         /**
37168          * @event regionresized
37169          * Fires when the user resizes a region.
37170          * @param {Roo.LayoutRegion} region The resized region
37171          * @param {Number} newSize The new size (width for east/west, height for north/south)
37172          */
37173         "regionresized" : true,
37174         /**
37175          * @event regioncollapsed
37176          * Fires when a region is collapsed.
37177          * @param {Roo.LayoutRegion} region The collapsed region
37178          */
37179         "regioncollapsed" : true,
37180         /**
37181          * @event regionexpanded
37182          * Fires when a region is expanded.
37183          * @param {Roo.LayoutRegion} region The expanded region
37184          */
37185         "regionexpanded" : true
37186     });
37187     this.updating = false;
37188
37189     if (config.el) {
37190         this.el = Roo.get(config.el);
37191         this.initEvents();
37192     }
37193
37194 };
37195
37196 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37197
37198
37199     regions : null,
37200
37201     monitorWindowResize : true,
37202
37203
37204     updating : false,
37205
37206
37207     onRender : function(ct, position)
37208     {
37209         if(!this.el){
37210             this.el = Roo.get(ct);
37211             this.initEvents();
37212         }
37213         //this.fireEvent('render',this);
37214     },
37215
37216
37217     initEvents: function()
37218     {
37219
37220
37221         // ie scrollbar fix
37222         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37223             document.body.scroll = "no";
37224         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37225             this.el.position('relative');
37226         }
37227         this.id = this.el.id;
37228         this.el.addClass("roo-layout-container");
37229         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37230         if(this.el.dom != document.body ) {
37231             this.el.on('resize', this.layout,this);
37232             this.el.on('show', this.layout,this);
37233         }
37234
37235     },
37236
37237     /**
37238      * Returns true if this layout is currently being updated
37239      * @return {Boolean}
37240      */
37241     isUpdating : function(){
37242         return this.updating;
37243     },
37244
37245     /**
37246      * Suspend the LayoutManager from doing auto-layouts while
37247      * making multiple add or remove calls
37248      */
37249     beginUpdate : function(){
37250         this.updating = true;
37251     },
37252
37253     /**
37254      * Restore auto-layouts and optionally disable the manager from performing a layout
37255      * @param {Boolean} noLayout true to disable a layout update
37256      */
37257     endUpdate : function(noLayout){
37258         this.updating = false;
37259         if(!noLayout){
37260             this.layout();
37261         }
37262     },
37263
37264     layout: function(){
37265         // abstract...
37266     },
37267
37268     onRegionResized : function(region, newSize){
37269         this.fireEvent("regionresized", region, newSize);
37270         this.layout();
37271     },
37272
37273     onRegionCollapsed : function(region){
37274         this.fireEvent("regioncollapsed", region);
37275     },
37276
37277     onRegionExpanded : function(region){
37278         this.fireEvent("regionexpanded", region);
37279     },
37280
37281     /**
37282      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37283      * performs box-model adjustments.
37284      * @return {Object} The size as an object {width: (the width), height: (the height)}
37285      */
37286     getViewSize : function()
37287     {
37288         var size;
37289         if(this.el.dom != document.body){
37290             size = this.el.getSize();
37291         }else{
37292             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37293         }
37294         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37295         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37296         return size;
37297     },
37298
37299     /**
37300      * Returns the Element this layout is bound to.
37301      * @return {Roo.Element}
37302      */
37303     getEl : function(){
37304         return this.el;
37305     },
37306
37307     /**
37308      * Returns the specified region.
37309      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37310      * @return {Roo.LayoutRegion}
37311      */
37312     getRegion : function(target){
37313         return this.regions[target.toLowerCase()];
37314     },
37315
37316     onWindowResize : function(){
37317         if(this.monitorWindowResize){
37318             this.layout();
37319         }
37320     }
37321 });
37322 /*
37323  * Based on:
37324  * Ext JS Library 1.1.1
37325  * Copyright(c) 2006-2007, Ext JS, LLC.
37326  *
37327  * Originally Released Under LGPL - original licence link has changed is not relivant.
37328  *
37329  * Fork - LGPL
37330  * <script type="text/javascript">
37331  */
37332 /**
37333  * @class Roo.bootstrap.layout.Border
37334  * @extends Roo.bootstrap.layout.Manager
37335  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37336  * please see: examples/bootstrap/nested.html<br><br>
37337  
37338 <b>The container the layout is rendered into can be either the body element or any other element.
37339 If it is not the body element, the container needs to either be an absolute positioned element,
37340 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37341 the container size if it is not the body element.</b>
37342
37343 * @constructor
37344 * Create a new Border
37345 * @param {Object} config Configuration options
37346  */
37347 Roo.bootstrap.layout.Border = function(config){
37348     config = config || {};
37349     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37350     
37351     
37352     
37353     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37354         if(config[region]){
37355             config[region].region = region;
37356             this.addRegion(config[region]);
37357         }
37358     },this);
37359     
37360 };
37361
37362 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37363
37364 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37365     
37366     parent : false, // this might point to a 'nest' or a ???
37367     
37368     /**
37369      * Creates and adds a new region if it doesn't already exist.
37370      * @param {String} target The target region key (north, south, east, west or center).
37371      * @param {Object} config The regions config object
37372      * @return {BorderLayoutRegion} The new region
37373      */
37374     addRegion : function(config)
37375     {
37376         if(!this.regions[config.region]){
37377             var r = this.factory(config);
37378             this.bindRegion(r);
37379         }
37380         return this.regions[config.region];
37381     },
37382
37383     // private (kinda)
37384     bindRegion : function(r){
37385         this.regions[r.config.region] = r;
37386         
37387         r.on("visibilitychange",    this.layout, this);
37388         r.on("paneladded",          this.layout, this);
37389         r.on("panelremoved",        this.layout, this);
37390         r.on("invalidated",         this.layout, this);
37391         r.on("resized",             this.onRegionResized, this);
37392         r.on("collapsed",           this.onRegionCollapsed, this);
37393         r.on("expanded",            this.onRegionExpanded, this);
37394     },
37395
37396     /**
37397      * Performs a layout update.
37398      */
37399     layout : function()
37400     {
37401         if(this.updating) {
37402             return;
37403         }
37404         
37405         // render all the rebions if they have not been done alreayd?
37406         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37407             if(this.regions[region] && !this.regions[region].bodyEl){
37408                 this.regions[region].onRender(this.el)
37409             }
37410         },this);
37411         
37412         var size = this.getViewSize();
37413         var w = size.width;
37414         var h = size.height;
37415         var centerW = w;
37416         var centerH = h;
37417         var centerY = 0;
37418         var centerX = 0;
37419         //var x = 0, y = 0;
37420
37421         var rs = this.regions;
37422         var north = rs["north"];
37423         var south = rs["south"]; 
37424         var west = rs["west"];
37425         var east = rs["east"];
37426         var center = rs["center"];
37427         //if(this.hideOnLayout){ // not supported anymore
37428             //c.el.setStyle("display", "none");
37429         //}
37430         if(north && north.isVisible()){
37431             var b = north.getBox();
37432             var m = north.getMargins();
37433             b.width = w - (m.left+m.right);
37434             b.x = m.left;
37435             b.y = m.top;
37436             centerY = b.height + b.y + m.bottom;
37437             centerH -= centerY;
37438             north.updateBox(this.safeBox(b));
37439         }
37440         if(south && south.isVisible()){
37441             var b = south.getBox();
37442             var m = south.getMargins();
37443             b.width = w - (m.left+m.right);
37444             b.x = m.left;
37445             var totalHeight = (b.height + m.top + m.bottom);
37446             b.y = h - totalHeight + m.top;
37447             centerH -= totalHeight;
37448             south.updateBox(this.safeBox(b));
37449         }
37450         if(west && west.isVisible()){
37451             var b = west.getBox();
37452             var m = west.getMargins();
37453             b.height = centerH - (m.top+m.bottom);
37454             b.x = m.left;
37455             b.y = centerY + m.top;
37456             var totalWidth = (b.width + m.left + m.right);
37457             centerX += totalWidth;
37458             centerW -= totalWidth;
37459             west.updateBox(this.safeBox(b));
37460         }
37461         if(east && east.isVisible()){
37462             var b = east.getBox();
37463             var m = east.getMargins();
37464             b.height = centerH - (m.top+m.bottom);
37465             var totalWidth = (b.width + m.left + m.right);
37466             b.x = w - totalWidth + m.left;
37467             b.y = centerY + m.top;
37468             centerW -= totalWidth;
37469             east.updateBox(this.safeBox(b));
37470         }
37471         if(center){
37472             var m = center.getMargins();
37473             var centerBox = {
37474                 x: centerX + m.left,
37475                 y: centerY + m.top,
37476                 width: centerW - (m.left+m.right),
37477                 height: centerH - (m.top+m.bottom)
37478             };
37479             //if(this.hideOnLayout){
37480                 //center.el.setStyle("display", "block");
37481             //}
37482             center.updateBox(this.safeBox(centerBox));
37483         }
37484         this.el.repaint();
37485         this.fireEvent("layout", this);
37486     },
37487
37488     // private
37489     safeBox : function(box){
37490         box.width = Math.max(0, box.width);
37491         box.height = Math.max(0, box.height);
37492         return box;
37493     },
37494
37495     /**
37496      * Adds a ContentPanel (or subclass) to this layout.
37497      * @param {String} target The target region key (north, south, east, west or center).
37498      * @param {Roo.ContentPanel} panel The panel to add
37499      * @return {Roo.ContentPanel} The added panel
37500      */
37501     add : function(target, panel){
37502          
37503         target = target.toLowerCase();
37504         return this.regions[target].add(panel);
37505     },
37506
37507     /**
37508      * Remove a ContentPanel (or subclass) to this layout.
37509      * @param {String} target The target region key (north, south, east, west or center).
37510      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37511      * @return {Roo.ContentPanel} The removed panel
37512      */
37513     remove : function(target, panel){
37514         target = target.toLowerCase();
37515         return this.regions[target].remove(panel);
37516     },
37517
37518     /**
37519      * Searches all regions for a panel with the specified id
37520      * @param {String} panelId
37521      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37522      */
37523     findPanel : function(panelId){
37524         var rs = this.regions;
37525         for(var target in rs){
37526             if(typeof rs[target] != "function"){
37527                 var p = rs[target].getPanel(panelId);
37528                 if(p){
37529                     return p;
37530                 }
37531             }
37532         }
37533         return null;
37534     },
37535
37536     /**
37537      * Searches all regions for a panel with the specified id and activates (shows) it.
37538      * @param {String/ContentPanel} panelId The panels id or the panel itself
37539      * @return {Roo.ContentPanel} The shown panel or null
37540      */
37541     showPanel : function(panelId) {
37542       var rs = this.regions;
37543       for(var target in rs){
37544          var r = rs[target];
37545          if(typeof r != "function"){
37546             if(r.hasPanel(panelId)){
37547                return r.showPanel(panelId);
37548             }
37549          }
37550       }
37551       return null;
37552    },
37553
37554    /**
37555      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37556      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37557      */
37558    /*
37559     restoreState : function(provider){
37560         if(!provider){
37561             provider = Roo.state.Manager;
37562         }
37563         var sm = new Roo.LayoutStateManager();
37564         sm.init(this, provider);
37565     },
37566 */
37567  
37568  
37569     /**
37570      * Adds a xtype elements to the layout.
37571      * <pre><code>
37572
37573 layout.addxtype({
37574        xtype : 'ContentPanel',
37575        region: 'west',
37576        items: [ .... ]
37577    }
37578 );
37579
37580 layout.addxtype({
37581         xtype : 'NestedLayoutPanel',
37582         region: 'west',
37583         layout: {
37584            center: { },
37585            west: { }   
37586         },
37587         items : [ ... list of content panels or nested layout panels.. ]
37588    }
37589 );
37590 </code></pre>
37591      * @param {Object} cfg Xtype definition of item to add.
37592      */
37593     addxtype : function(cfg)
37594     {
37595         // basically accepts a pannel...
37596         // can accept a layout region..!?!?
37597         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37598         
37599         
37600         // theory?  children can only be panels??
37601         
37602         //if (!cfg.xtype.match(/Panel$/)) {
37603         //    return false;
37604         //}
37605         var ret = false;
37606         
37607         if (typeof(cfg.region) == 'undefined') {
37608             Roo.log("Failed to add Panel, region was not set");
37609             Roo.log(cfg);
37610             return false;
37611         }
37612         var region = cfg.region;
37613         delete cfg.region;
37614         
37615           
37616         var xitems = [];
37617         if (cfg.items) {
37618             xitems = cfg.items;
37619             delete cfg.items;
37620         }
37621         var nb = false;
37622         
37623         if ( region == 'center') {
37624             Roo.log("Center: " + cfg.title);
37625         }
37626         
37627         
37628         switch(cfg.xtype) 
37629         {
37630             case 'Content':  // ContentPanel (el, cfg)
37631             case 'Scroll':  // ContentPanel (el, cfg)
37632             case 'View': 
37633                 cfg.autoCreate = cfg.autoCreate || true;
37634                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37635                 //} else {
37636                 //    var el = this.el.createChild();
37637                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37638                 //}
37639                 
37640                 this.add(region, ret);
37641                 break;
37642             
37643             /*
37644             case 'TreePanel': // our new panel!
37645                 cfg.el = this.el.createChild();
37646                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37647                 this.add(region, ret);
37648                 break;
37649             */
37650             
37651             case 'Nest': 
37652                 // create a new Layout (which is  a Border Layout...
37653                 
37654                 var clayout = cfg.layout;
37655                 clayout.el  = this.el.createChild();
37656                 clayout.items   = clayout.items  || [];
37657                 
37658                 delete cfg.layout;
37659                 
37660                 // replace this exitems with the clayout ones..
37661                 xitems = clayout.items;
37662                  
37663                 // force background off if it's in center...
37664                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37665                     cfg.background = false;
37666                 }
37667                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37668                 
37669                 
37670                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37671                 //console.log('adding nested layout panel '  + cfg.toSource());
37672                 this.add(region, ret);
37673                 nb = {}; /// find first...
37674                 break;
37675             
37676             case 'Grid':
37677                 
37678                 // needs grid and region
37679                 
37680                 //var el = this.getRegion(region).el.createChild();
37681                 /*
37682                  *var el = this.el.createChild();
37683                 // create the grid first...
37684                 cfg.grid.container = el;
37685                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37686                 */
37687                 
37688                 if (region == 'center' && this.active ) {
37689                     cfg.background = false;
37690                 }
37691                 
37692                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37693                 
37694                 this.add(region, ret);
37695                 /*
37696                 if (cfg.background) {
37697                     // render grid on panel activation (if panel background)
37698                     ret.on('activate', function(gp) {
37699                         if (!gp.grid.rendered) {
37700                     //        gp.grid.render(el);
37701                         }
37702                     });
37703                 } else {
37704                   //  cfg.grid.render(el);
37705                 }
37706                 */
37707                 break;
37708            
37709            
37710             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37711                 // it was the old xcomponent building that caused this before.
37712                 // espeically if border is the top element in the tree.
37713                 ret = this;
37714                 break; 
37715                 
37716                     
37717                 
37718                 
37719                 
37720             default:
37721                 /*
37722                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37723                     
37724                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37725                     this.add(region, ret);
37726                 } else {
37727                 */
37728                     Roo.log(cfg);
37729                     throw "Can not add '" + cfg.xtype + "' to Border";
37730                     return null;
37731              
37732                                 
37733              
37734         }
37735         this.beginUpdate();
37736         // add children..
37737         var region = '';
37738         var abn = {};
37739         Roo.each(xitems, function(i)  {
37740             region = nb && i.region ? i.region : false;
37741             
37742             var add = ret.addxtype(i);
37743            
37744             if (region) {
37745                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37746                 if (!i.background) {
37747                     abn[region] = nb[region] ;
37748                 }
37749             }
37750             
37751         });
37752         this.endUpdate();
37753
37754         // make the last non-background panel active..
37755         //if (nb) { Roo.log(abn); }
37756         if (nb) {
37757             
37758             for(var r in abn) {
37759                 region = this.getRegion(r);
37760                 if (region) {
37761                     // tried using nb[r], but it does not work..
37762                      
37763                     region.showPanel(abn[r]);
37764                    
37765                 }
37766             }
37767         }
37768         return ret;
37769         
37770     },
37771     
37772     
37773 // private
37774     factory : function(cfg)
37775     {
37776         
37777         var validRegions = Roo.bootstrap.layout.Border.regions;
37778
37779         var target = cfg.region;
37780         cfg.mgr = this;
37781         
37782         var r = Roo.bootstrap.layout;
37783         Roo.log(target);
37784         switch(target){
37785             case "north":
37786                 return new r.North(cfg);
37787             case "south":
37788                 return new r.South(cfg);
37789             case "east":
37790                 return new r.East(cfg);
37791             case "west":
37792                 return new r.West(cfg);
37793             case "center":
37794                 return new r.Center(cfg);
37795         }
37796         throw 'Layout region "'+target+'" not supported.';
37797     }
37798     
37799     
37800 });
37801  /*
37802  * Based on:
37803  * Ext JS Library 1.1.1
37804  * Copyright(c) 2006-2007, Ext JS, LLC.
37805  *
37806  * Originally Released Under LGPL - original licence link has changed is not relivant.
37807  *
37808  * Fork - LGPL
37809  * <script type="text/javascript">
37810  */
37811  
37812 /**
37813  * @class Roo.bootstrap.layout.Basic
37814  * @extends Roo.util.Observable
37815  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37816  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37817  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37818  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37819  * @cfg {string}   region  the region that it inhabits..
37820  * @cfg {bool}   skipConfig skip config?
37821  * 
37822
37823  */
37824 Roo.bootstrap.layout.Basic = function(config){
37825     
37826     this.mgr = config.mgr;
37827     
37828     this.position = config.region;
37829     
37830     var skipConfig = config.skipConfig;
37831     
37832     this.events = {
37833         /**
37834          * @scope Roo.BasicLayoutRegion
37835          */
37836         
37837         /**
37838          * @event beforeremove
37839          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37840          * @param {Roo.LayoutRegion} this
37841          * @param {Roo.ContentPanel} panel The panel
37842          * @param {Object} e The cancel event object
37843          */
37844         "beforeremove" : true,
37845         /**
37846          * @event invalidated
37847          * Fires when the layout for this region is changed.
37848          * @param {Roo.LayoutRegion} this
37849          */
37850         "invalidated" : true,
37851         /**
37852          * @event visibilitychange
37853          * Fires when this region is shown or hidden 
37854          * @param {Roo.LayoutRegion} this
37855          * @param {Boolean} visibility true or false
37856          */
37857         "visibilitychange" : true,
37858         /**
37859          * @event paneladded
37860          * Fires when a panel is added. 
37861          * @param {Roo.LayoutRegion} this
37862          * @param {Roo.ContentPanel} panel The panel
37863          */
37864         "paneladded" : true,
37865         /**
37866          * @event panelremoved
37867          * Fires when a panel is removed. 
37868          * @param {Roo.LayoutRegion} this
37869          * @param {Roo.ContentPanel} panel The panel
37870          */
37871         "panelremoved" : true,
37872         /**
37873          * @event beforecollapse
37874          * Fires when this region before collapse.
37875          * @param {Roo.LayoutRegion} this
37876          */
37877         "beforecollapse" : true,
37878         /**
37879          * @event collapsed
37880          * Fires when this region is collapsed.
37881          * @param {Roo.LayoutRegion} this
37882          */
37883         "collapsed" : true,
37884         /**
37885          * @event expanded
37886          * Fires when this region is expanded.
37887          * @param {Roo.LayoutRegion} this
37888          */
37889         "expanded" : true,
37890         /**
37891          * @event slideshow
37892          * Fires when this region is slid into view.
37893          * @param {Roo.LayoutRegion} this
37894          */
37895         "slideshow" : true,
37896         /**
37897          * @event slidehide
37898          * Fires when this region slides out of view. 
37899          * @param {Roo.LayoutRegion} this
37900          */
37901         "slidehide" : true,
37902         /**
37903          * @event panelactivated
37904          * Fires when a panel is activated. 
37905          * @param {Roo.LayoutRegion} this
37906          * @param {Roo.ContentPanel} panel The activated panel
37907          */
37908         "panelactivated" : true,
37909         /**
37910          * @event resized
37911          * Fires when the user resizes this region. 
37912          * @param {Roo.LayoutRegion} this
37913          * @param {Number} newSize The new size (width for east/west, height for north/south)
37914          */
37915         "resized" : true
37916     };
37917     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37918     this.panels = new Roo.util.MixedCollection();
37919     this.panels.getKey = this.getPanelId.createDelegate(this);
37920     this.box = null;
37921     this.activePanel = null;
37922     // ensure listeners are added...
37923     
37924     if (config.listeners || config.events) {
37925         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37926             listeners : config.listeners || {},
37927             events : config.events || {}
37928         });
37929     }
37930     
37931     if(skipConfig !== true){
37932         this.applyConfig(config);
37933     }
37934 };
37935
37936 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37937 {
37938     getPanelId : function(p){
37939         return p.getId();
37940     },
37941     
37942     applyConfig : function(config){
37943         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37944         this.config = config;
37945         
37946     },
37947     
37948     /**
37949      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37950      * the width, for horizontal (north, south) the height.
37951      * @param {Number} newSize The new width or height
37952      */
37953     resizeTo : function(newSize){
37954         var el = this.el ? this.el :
37955                  (this.activePanel ? this.activePanel.getEl() : null);
37956         if(el){
37957             switch(this.position){
37958                 case "east":
37959                 case "west":
37960                     el.setWidth(newSize);
37961                     this.fireEvent("resized", this, newSize);
37962                 break;
37963                 case "north":
37964                 case "south":
37965                     el.setHeight(newSize);
37966                     this.fireEvent("resized", this, newSize);
37967                 break;                
37968             }
37969         }
37970     },
37971     
37972     getBox : function(){
37973         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37974     },
37975     
37976     getMargins : function(){
37977         return this.margins;
37978     },
37979     
37980     updateBox : function(box){
37981         this.box = box;
37982         var el = this.activePanel.getEl();
37983         el.dom.style.left = box.x + "px";
37984         el.dom.style.top = box.y + "px";
37985         this.activePanel.setSize(box.width, box.height);
37986     },
37987     
37988     /**
37989      * Returns the container element for this region.
37990      * @return {Roo.Element}
37991      */
37992     getEl : function(){
37993         return this.activePanel;
37994     },
37995     
37996     /**
37997      * Returns true if this region is currently visible.
37998      * @return {Boolean}
37999      */
38000     isVisible : function(){
38001         return this.activePanel ? true : false;
38002     },
38003     
38004     setActivePanel : function(panel){
38005         panel = this.getPanel(panel);
38006         if(this.activePanel && this.activePanel != panel){
38007             this.activePanel.setActiveState(false);
38008             this.activePanel.getEl().setLeftTop(-10000,-10000);
38009         }
38010         this.activePanel = panel;
38011         panel.setActiveState(true);
38012         if(this.box){
38013             panel.setSize(this.box.width, this.box.height);
38014         }
38015         this.fireEvent("panelactivated", this, panel);
38016         this.fireEvent("invalidated");
38017     },
38018     
38019     /**
38020      * Show the specified panel.
38021      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38022      * @return {Roo.ContentPanel} The shown panel or null
38023      */
38024     showPanel : function(panel){
38025         panel = this.getPanel(panel);
38026         if(panel){
38027             this.setActivePanel(panel);
38028         }
38029         return panel;
38030     },
38031     
38032     /**
38033      * Get the active panel for this region.
38034      * @return {Roo.ContentPanel} The active panel or null
38035      */
38036     getActivePanel : function(){
38037         return this.activePanel;
38038     },
38039     
38040     /**
38041      * Add the passed ContentPanel(s)
38042      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38043      * @return {Roo.ContentPanel} The panel added (if only one was added)
38044      */
38045     add : function(panel){
38046         if(arguments.length > 1){
38047             for(var i = 0, len = arguments.length; i < len; i++) {
38048                 this.add(arguments[i]);
38049             }
38050             return null;
38051         }
38052         if(this.hasPanel(panel)){
38053             this.showPanel(panel);
38054             return panel;
38055         }
38056         var el = panel.getEl();
38057         if(el.dom.parentNode != this.mgr.el.dom){
38058             this.mgr.el.dom.appendChild(el.dom);
38059         }
38060         if(panel.setRegion){
38061             panel.setRegion(this);
38062         }
38063         this.panels.add(panel);
38064         el.setStyle("position", "absolute");
38065         if(!panel.background){
38066             this.setActivePanel(panel);
38067             if(this.config.initialSize && this.panels.getCount()==1){
38068                 this.resizeTo(this.config.initialSize);
38069             }
38070         }
38071         this.fireEvent("paneladded", this, panel);
38072         return panel;
38073     },
38074     
38075     /**
38076      * Returns true if the panel is in this region.
38077      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38078      * @return {Boolean}
38079      */
38080     hasPanel : function(panel){
38081         if(typeof panel == "object"){ // must be panel obj
38082             panel = panel.getId();
38083         }
38084         return this.getPanel(panel) ? true : false;
38085     },
38086     
38087     /**
38088      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38089      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38090      * @param {Boolean} preservePanel Overrides the config preservePanel option
38091      * @return {Roo.ContentPanel} The panel that was removed
38092      */
38093     remove : function(panel, preservePanel){
38094         panel = this.getPanel(panel);
38095         if(!panel){
38096             return null;
38097         }
38098         var e = {};
38099         this.fireEvent("beforeremove", this, panel, e);
38100         if(e.cancel === true){
38101             return null;
38102         }
38103         var panelId = panel.getId();
38104         this.panels.removeKey(panelId);
38105         return panel;
38106     },
38107     
38108     /**
38109      * Returns the panel specified or null if it's not in this region.
38110      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38111      * @return {Roo.ContentPanel}
38112      */
38113     getPanel : function(id){
38114         if(typeof id == "object"){ // must be panel obj
38115             return id;
38116         }
38117         return this.panels.get(id);
38118     },
38119     
38120     /**
38121      * Returns this regions position (north/south/east/west/center).
38122      * @return {String} 
38123      */
38124     getPosition: function(){
38125         return this.position;    
38126     }
38127 });/*
38128  * Based on:
38129  * Ext JS Library 1.1.1
38130  * Copyright(c) 2006-2007, Ext JS, LLC.
38131  *
38132  * Originally Released Under LGPL - original licence link has changed is not relivant.
38133  *
38134  * Fork - LGPL
38135  * <script type="text/javascript">
38136  */
38137  
38138 /**
38139  * @class Roo.bootstrap.layout.Region
38140  * @extends Roo.bootstrap.layout.Basic
38141  * This class represents a region in a layout manager.
38142  
38143  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38144  * @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})
38145  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38146  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38147  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38148  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38149  * @cfg {String}    title           The title for the region (overrides panel titles)
38150  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38151  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38152  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38153  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38154  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38155  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38156  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38157  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38158  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38159  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38160
38161  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38162  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38163  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38164  * @cfg {Number}    width           For East/West panels
38165  * @cfg {Number}    height          For North/South panels
38166  * @cfg {Boolean}   split           To show the splitter
38167  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38168  * 
38169  * @cfg {string}   cls             Extra CSS classes to add to region
38170  * 
38171  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38172  * @cfg {string}   region  the region that it inhabits..
38173  *
38174
38175  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38176  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38177
38178  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38179  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38180  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38181  */
38182 Roo.bootstrap.layout.Region = function(config)
38183 {
38184     this.applyConfig(config);
38185
38186     var mgr = config.mgr;
38187     var pos = config.region;
38188     config.skipConfig = true;
38189     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38190     
38191     if (mgr.el) {
38192         this.onRender(mgr.el);   
38193     }
38194      
38195     this.visible = true;
38196     this.collapsed = false;
38197     this.unrendered_panels = [];
38198 };
38199
38200 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38201
38202     position: '', // set by wrapper (eg. north/south etc..)
38203     unrendered_panels : null,  // unrendered panels.
38204     
38205     tabPosition : false,
38206     
38207     mgr: false, // points to 'Border'
38208     
38209     
38210     createBody : function(){
38211         /** This region's body element 
38212         * @type Roo.Element */
38213         this.bodyEl = this.el.createChild({
38214                 tag: "div",
38215                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38216         });
38217     },
38218
38219     onRender: function(ctr, pos)
38220     {
38221         var dh = Roo.DomHelper;
38222         /** This region's container element 
38223         * @type Roo.Element */
38224         this.el = dh.append(ctr.dom, {
38225                 tag: "div",
38226                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38227             }, true);
38228         /** This region's title element 
38229         * @type Roo.Element */
38230     
38231         this.titleEl = dh.append(this.el.dom,  {
38232                 tag: "div",
38233                 unselectable: "on",
38234                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38235                 children:[
38236                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38237                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38238                 ]
38239             }, true);
38240         
38241         this.titleEl.enableDisplayMode();
38242         /** This region's title text element 
38243         * @type HTMLElement */
38244         this.titleTextEl = this.titleEl.dom.firstChild;
38245         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38246         /*
38247         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38248         this.closeBtn.enableDisplayMode();
38249         this.closeBtn.on("click", this.closeClicked, this);
38250         this.closeBtn.hide();
38251     */
38252         this.createBody(this.config);
38253         if(this.config.hideWhenEmpty){
38254             this.hide();
38255             this.on("paneladded", this.validateVisibility, this);
38256             this.on("panelremoved", this.validateVisibility, this);
38257         }
38258         if(this.autoScroll){
38259             this.bodyEl.setStyle("overflow", "auto");
38260         }else{
38261             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38262         }
38263         //if(c.titlebar !== false){
38264             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38265                 this.titleEl.hide();
38266             }else{
38267                 this.titleEl.show();
38268                 if(this.config.title){
38269                     this.titleTextEl.innerHTML = this.config.title;
38270                 }
38271             }
38272         //}
38273         if(this.config.collapsed){
38274             this.collapse(true);
38275         }
38276         if(this.config.hidden){
38277             this.hide();
38278         }
38279         
38280         if (this.unrendered_panels && this.unrendered_panels.length) {
38281             for (var i =0;i< this.unrendered_panels.length; i++) {
38282                 this.add(this.unrendered_panels[i]);
38283             }
38284             this.unrendered_panels = null;
38285             
38286         }
38287         
38288     },
38289     
38290     applyConfig : function(c)
38291     {
38292         /*
38293          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38294             var dh = Roo.DomHelper;
38295             if(c.titlebar !== false){
38296                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38297                 this.collapseBtn.on("click", this.collapse, this);
38298                 this.collapseBtn.enableDisplayMode();
38299                 /*
38300                 if(c.showPin === true || this.showPin){
38301                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38302                     this.stickBtn.enableDisplayMode();
38303                     this.stickBtn.on("click", this.expand, this);
38304                     this.stickBtn.hide();
38305                 }
38306                 
38307             }
38308             */
38309             /** This region's collapsed element
38310             * @type Roo.Element */
38311             /*
38312              *
38313             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38314                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38315             ]}, true);
38316             
38317             if(c.floatable !== false){
38318                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38319                this.collapsedEl.on("click", this.collapseClick, this);
38320             }
38321
38322             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38323                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38324                    id: "message", unselectable: "on", style:{"float":"left"}});
38325                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38326              }
38327             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38328             this.expandBtn.on("click", this.expand, this);
38329             
38330         }
38331         
38332         if(this.collapseBtn){
38333             this.collapseBtn.setVisible(c.collapsible == true);
38334         }
38335         
38336         this.cmargins = c.cmargins || this.cmargins ||
38337                          (this.position == "west" || this.position == "east" ?
38338                              {top: 0, left: 2, right:2, bottom: 0} :
38339                              {top: 2, left: 0, right:0, bottom: 2});
38340         */
38341         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38342         
38343         
38344         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38345         
38346         this.autoScroll = c.autoScroll || false;
38347         
38348         
38349        
38350         
38351         this.duration = c.duration || .30;
38352         this.slideDuration = c.slideDuration || .45;
38353         this.config = c;
38354        
38355     },
38356     /**
38357      * Returns true if this region is currently visible.
38358      * @return {Boolean}
38359      */
38360     isVisible : function(){
38361         return this.visible;
38362     },
38363
38364     /**
38365      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38366      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38367      */
38368     //setCollapsedTitle : function(title){
38369     //    title = title || "&#160;";
38370      //   if(this.collapsedTitleTextEl){
38371       //      this.collapsedTitleTextEl.innerHTML = title;
38372        // }
38373     //},
38374
38375     getBox : function(){
38376         var b;
38377       //  if(!this.collapsed){
38378             b = this.el.getBox(false, true);
38379        // }else{
38380           //  b = this.collapsedEl.getBox(false, true);
38381         //}
38382         return b;
38383     },
38384
38385     getMargins : function(){
38386         return this.margins;
38387         //return this.collapsed ? this.cmargins : this.margins;
38388     },
38389 /*
38390     highlight : function(){
38391         this.el.addClass("x-layout-panel-dragover");
38392     },
38393
38394     unhighlight : function(){
38395         this.el.removeClass("x-layout-panel-dragover");
38396     },
38397 */
38398     updateBox : function(box)
38399     {
38400         if (!this.bodyEl) {
38401             return; // not rendered yet..
38402         }
38403         
38404         this.box = box;
38405         if(!this.collapsed){
38406             this.el.dom.style.left = box.x + "px";
38407             this.el.dom.style.top = box.y + "px";
38408             this.updateBody(box.width, box.height);
38409         }else{
38410             this.collapsedEl.dom.style.left = box.x + "px";
38411             this.collapsedEl.dom.style.top = box.y + "px";
38412             this.collapsedEl.setSize(box.width, box.height);
38413         }
38414         if(this.tabs){
38415             this.tabs.autoSizeTabs();
38416         }
38417     },
38418
38419     updateBody : function(w, h)
38420     {
38421         if(w !== null){
38422             this.el.setWidth(w);
38423             w -= this.el.getBorderWidth("rl");
38424             if(this.config.adjustments){
38425                 w += this.config.adjustments[0];
38426             }
38427         }
38428         if(h !== null && h > 0){
38429             this.el.setHeight(h);
38430             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38431             h -= this.el.getBorderWidth("tb");
38432             if(this.config.adjustments){
38433                 h += this.config.adjustments[1];
38434             }
38435             this.bodyEl.setHeight(h);
38436             if(this.tabs){
38437                 h = this.tabs.syncHeight(h);
38438             }
38439         }
38440         if(this.panelSize){
38441             w = w !== null ? w : this.panelSize.width;
38442             h = h !== null ? h : this.panelSize.height;
38443         }
38444         if(this.activePanel){
38445             var el = this.activePanel.getEl();
38446             w = w !== null ? w : el.getWidth();
38447             h = h !== null ? h : el.getHeight();
38448             this.panelSize = {width: w, height: h};
38449             this.activePanel.setSize(w, h);
38450         }
38451         if(Roo.isIE && this.tabs){
38452             this.tabs.el.repaint();
38453         }
38454     },
38455
38456     /**
38457      * Returns the container element for this region.
38458      * @return {Roo.Element}
38459      */
38460     getEl : function(){
38461         return this.el;
38462     },
38463
38464     /**
38465      * Hides this region.
38466      */
38467     hide : function(){
38468         //if(!this.collapsed){
38469             this.el.dom.style.left = "-2000px";
38470             this.el.hide();
38471         //}else{
38472          //   this.collapsedEl.dom.style.left = "-2000px";
38473          //   this.collapsedEl.hide();
38474        // }
38475         this.visible = false;
38476         this.fireEvent("visibilitychange", this, false);
38477     },
38478
38479     /**
38480      * Shows this region if it was previously hidden.
38481      */
38482     show : function(){
38483         //if(!this.collapsed){
38484             this.el.show();
38485         //}else{
38486         //    this.collapsedEl.show();
38487        // }
38488         this.visible = true;
38489         this.fireEvent("visibilitychange", this, true);
38490     },
38491 /*
38492     closeClicked : function(){
38493         if(this.activePanel){
38494             this.remove(this.activePanel);
38495         }
38496     },
38497
38498     collapseClick : function(e){
38499         if(this.isSlid){
38500            e.stopPropagation();
38501            this.slideIn();
38502         }else{
38503            e.stopPropagation();
38504            this.slideOut();
38505         }
38506     },
38507 */
38508     /**
38509      * Collapses this region.
38510      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38511      */
38512     /*
38513     collapse : function(skipAnim, skipCheck = false){
38514         if(this.collapsed) {
38515             return;
38516         }
38517         
38518         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38519             
38520             this.collapsed = true;
38521             if(this.split){
38522                 this.split.el.hide();
38523             }
38524             if(this.config.animate && skipAnim !== true){
38525                 this.fireEvent("invalidated", this);
38526                 this.animateCollapse();
38527             }else{
38528                 this.el.setLocation(-20000,-20000);
38529                 this.el.hide();
38530                 this.collapsedEl.show();
38531                 this.fireEvent("collapsed", this);
38532                 this.fireEvent("invalidated", this);
38533             }
38534         }
38535         
38536     },
38537 */
38538     animateCollapse : function(){
38539         // overridden
38540     },
38541
38542     /**
38543      * Expands this region if it was previously collapsed.
38544      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38545      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38546      */
38547     /*
38548     expand : function(e, skipAnim){
38549         if(e) {
38550             e.stopPropagation();
38551         }
38552         if(!this.collapsed || this.el.hasActiveFx()) {
38553             return;
38554         }
38555         if(this.isSlid){
38556             this.afterSlideIn();
38557             skipAnim = true;
38558         }
38559         this.collapsed = false;
38560         if(this.config.animate && skipAnim !== true){
38561             this.animateExpand();
38562         }else{
38563             this.el.show();
38564             if(this.split){
38565                 this.split.el.show();
38566             }
38567             this.collapsedEl.setLocation(-2000,-2000);
38568             this.collapsedEl.hide();
38569             this.fireEvent("invalidated", this);
38570             this.fireEvent("expanded", this);
38571         }
38572     },
38573 */
38574     animateExpand : function(){
38575         // overridden
38576     },
38577
38578     initTabs : function()
38579     {
38580         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38581         
38582         var ts = new Roo.bootstrap.panel.Tabs({
38583             el: this.bodyEl.dom,
38584             region : this,
38585             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38586             disableTooltips: this.config.disableTabTips,
38587             toolbar : this.config.toolbar
38588         });
38589         
38590         if(this.config.hideTabs){
38591             ts.stripWrap.setDisplayed(false);
38592         }
38593         this.tabs = ts;
38594         ts.resizeTabs = this.config.resizeTabs === true;
38595         ts.minTabWidth = this.config.minTabWidth || 40;
38596         ts.maxTabWidth = this.config.maxTabWidth || 250;
38597         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38598         ts.monitorResize = false;
38599         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38600         ts.bodyEl.addClass('roo-layout-tabs-body');
38601         this.panels.each(this.initPanelAsTab, this);
38602     },
38603
38604     initPanelAsTab : function(panel){
38605         var ti = this.tabs.addTab(
38606             panel.getEl().id,
38607             panel.getTitle(),
38608             null,
38609             this.config.closeOnTab && panel.isClosable(),
38610             panel.tpl
38611         );
38612         if(panel.tabTip !== undefined){
38613             ti.setTooltip(panel.tabTip);
38614         }
38615         ti.on("activate", function(){
38616               this.setActivePanel(panel);
38617         }, this);
38618         
38619         if(this.config.closeOnTab){
38620             ti.on("beforeclose", function(t, e){
38621                 e.cancel = true;
38622                 this.remove(panel);
38623             }, this);
38624         }
38625         
38626         panel.tabItem = ti;
38627         
38628         return ti;
38629     },
38630
38631     updatePanelTitle : function(panel, title)
38632     {
38633         if(this.activePanel == panel){
38634             this.updateTitle(title);
38635         }
38636         if(this.tabs){
38637             var ti = this.tabs.getTab(panel.getEl().id);
38638             ti.setText(title);
38639             if(panel.tabTip !== undefined){
38640                 ti.setTooltip(panel.tabTip);
38641             }
38642         }
38643     },
38644
38645     updateTitle : function(title){
38646         if(this.titleTextEl && !this.config.title){
38647             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38648         }
38649     },
38650
38651     setActivePanel : function(panel)
38652     {
38653         panel = this.getPanel(panel);
38654         if(this.activePanel && this.activePanel != panel){
38655             if(this.activePanel.setActiveState(false) === false){
38656                 return;
38657             }
38658         }
38659         this.activePanel = panel;
38660         panel.setActiveState(true);
38661         if(this.panelSize){
38662             panel.setSize(this.panelSize.width, this.panelSize.height);
38663         }
38664         if(this.closeBtn){
38665             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38666         }
38667         this.updateTitle(panel.getTitle());
38668         if(this.tabs){
38669             this.fireEvent("invalidated", this);
38670         }
38671         this.fireEvent("panelactivated", this, panel);
38672     },
38673
38674     /**
38675      * Shows the specified panel.
38676      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38677      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38678      */
38679     showPanel : function(panel)
38680     {
38681         panel = this.getPanel(panel);
38682         if(panel){
38683             if(this.tabs){
38684                 var tab = this.tabs.getTab(panel.getEl().id);
38685                 if(tab.isHidden()){
38686                     this.tabs.unhideTab(tab.id);
38687                 }
38688                 tab.activate();
38689             }else{
38690                 this.setActivePanel(panel);
38691             }
38692         }
38693         return panel;
38694     },
38695
38696     /**
38697      * Get the active panel for this region.
38698      * @return {Roo.ContentPanel} The active panel or null
38699      */
38700     getActivePanel : function(){
38701         return this.activePanel;
38702     },
38703
38704     validateVisibility : function(){
38705         if(this.panels.getCount() < 1){
38706             this.updateTitle("&#160;");
38707             this.closeBtn.hide();
38708             this.hide();
38709         }else{
38710             if(!this.isVisible()){
38711                 this.show();
38712             }
38713         }
38714     },
38715
38716     /**
38717      * Adds the passed ContentPanel(s) to this region.
38718      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38719      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38720      */
38721     add : function(panel)
38722     {
38723         if(arguments.length > 1){
38724             for(var i = 0, len = arguments.length; i < len; i++) {
38725                 this.add(arguments[i]);
38726             }
38727             return null;
38728         }
38729         
38730         // if we have not been rendered yet, then we can not really do much of this..
38731         if (!this.bodyEl) {
38732             this.unrendered_panels.push(panel);
38733             return panel;
38734         }
38735         
38736         
38737         
38738         
38739         if(this.hasPanel(panel)){
38740             this.showPanel(panel);
38741             return panel;
38742         }
38743         panel.setRegion(this);
38744         this.panels.add(panel);
38745        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38746             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38747             // and hide them... ???
38748             this.bodyEl.dom.appendChild(panel.getEl().dom);
38749             if(panel.background !== true){
38750                 this.setActivePanel(panel);
38751             }
38752             this.fireEvent("paneladded", this, panel);
38753             return panel;
38754         }
38755         */
38756         if(!this.tabs){
38757             this.initTabs();
38758         }else{
38759             this.initPanelAsTab(panel);
38760         }
38761         
38762         
38763         if(panel.background !== true){
38764             this.tabs.activate(panel.getEl().id);
38765         }
38766         this.fireEvent("paneladded", this, panel);
38767         return panel;
38768     },
38769
38770     /**
38771      * Hides the tab for the specified panel.
38772      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38773      */
38774     hidePanel : function(panel){
38775         if(this.tabs && (panel = this.getPanel(panel))){
38776             this.tabs.hideTab(panel.getEl().id);
38777         }
38778     },
38779
38780     /**
38781      * Unhides the tab for a previously hidden panel.
38782      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38783      */
38784     unhidePanel : function(panel){
38785         if(this.tabs && (panel = this.getPanel(panel))){
38786             this.tabs.unhideTab(panel.getEl().id);
38787         }
38788     },
38789
38790     clearPanels : function(){
38791         while(this.panels.getCount() > 0){
38792              this.remove(this.panels.first());
38793         }
38794     },
38795
38796     /**
38797      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38799      * @param {Boolean} preservePanel Overrides the config preservePanel option
38800      * @return {Roo.ContentPanel} The panel that was removed
38801      */
38802     remove : function(panel, preservePanel)
38803     {
38804         panel = this.getPanel(panel);
38805         if(!panel){
38806             return null;
38807         }
38808         var e = {};
38809         this.fireEvent("beforeremove", this, panel, e);
38810         if(e.cancel === true){
38811             return null;
38812         }
38813         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38814         var panelId = panel.getId();
38815         this.panels.removeKey(panelId);
38816         if(preservePanel){
38817             document.body.appendChild(panel.getEl().dom);
38818         }
38819         if(this.tabs){
38820             this.tabs.removeTab(panel.getEl().id);
38821         }else if (!preservePanel){
38822             this.bodyEl.dom.removeChild(panel.getEl().dom);
38823         }
38824         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38825             var p = this.panels.first();
38826             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38827             tempEl.appendChild(p.getEl().dom);
38828             this.bodyEl.update("");
38829             this.bodyEl.dom.appendChild(p.getEl().dom);
38830             tempEl = null;
38831             this.updateTitle(p.getTitle());
38832             this.tabs = null;
38833             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38834             this.setActivePanel(p);
38835         }
38836         panel.setRegion(null);
38837         if(this.activePanel == panel){
38838             this.activePanel = null;
38839         }
38840         if(this.config.autoDestroy !== false && preservePanel !== true){
38841             try{panel.destroy();}catch(e){}
38842         }
38843         this.fireEvent("panelremoved", this, panel);
38844         return panel;
38845     },
38846
38847     /**
38848      * Returns the TabPanel component used by this region
38849      * @return {Roo.TabPanel}
38850      */
38851     getTabs : function(){
38852         return this.tabs;
38853     },
38854
38855     createTool : function(parentEl, className){
38856         var btn = Roo.DomHelper.append(parentEl, {
38857             tag: "div",
38858             cls: "x-layout-tools-button",
38859             children: [ {
38860                 tag: "div",
38861                 cls: "roo-layout-tools-button-inner " + className,
38862                 html: "&#160;"
38863             }]
38864         }, true);
38865         btn.addClassOnOver("roo-layout-tools-button-over");
38866         return btn;
38867     }
38868 });/*
38869  * Based on:
38870  * Ext JS Library 1.1.1
38871  * Copyright(c) 2006-2007, Ext JS, LLC.
38872  *
38873  * Originally Released Under LGPL - original licence link has changed is not relivant.
38874  *
38875  * Fork - LGPL
38876  * <script type="text/javascript">
38877  */
38878  
38879
38880
38881 /**
38882  * @class Roo.SplitLayoutRegion
38883  * @extends Roo.LayoutRegion
38884  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38885  */
38886 Roo.bootstrap.layout.Split = function(config){
38887     this.cursor = config.cursor;
38888     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38889 };
38890
38891 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38892 {
38893     splitTip : "Drag to resize.",
38894     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38895     useSplitTips : false,
38896
38897     applyConfig : function(config){
38898         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38899     },
38900     
38901     onRender : function(ctr,pos) {
38902         
38903         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38904         if(!this.config.split){
38905             return;
38906         }
38907         if(!this.split){
38908             
38909             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38910                             tag: "div",
38911                             id: this.el.id + "-split",
38912                             cls: "roo-layout-split roo-layout-split-"+this.position,
38913                             html: "&#160;"
38914             });
38915             /** The SplitBar for this region 
38916             * @type Roo.SplitBar */
38917             // does not exist yet...
38918             Roo.log([this.position, this.orientation]);
38919             
38920             this.split = new Roo.bootstrap.SplitBar({
38921                 dragElement : splitEl,
38922                 resizingElement: this.el,
38923                 orientation : this.orientation
38924             });
38925             
38926             this.split.on("moved", this.onSplitMove, this);
38927             this.split.useShim = this.config.useShim === true;
38928             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38929             if(this.useSplitTips){
38930                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38931             }
38932             //if(config.collapsible){
38933             //    this.split.el.on("dblclick", this.collapse,  this);
38934             //}
38935         }
38936         if(typeof this.config.minSize != "undefined"){
38937             this.split.minSize = this.config.minSize;
38938         }
38939         if(typeof this.config.maxSize != "undefined"){
38940             this.split.maxSize = this.config.maxSize;
38941         }
38942         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38943             this.hideSplitter();
38944         }
38945         
38946     },
38947
38948     getHMaxSize : function(){
38949          var cmax = this.config.maxSize || 10000;
38950          var center = this.mgr.getRegion("center");
38951          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38952     },
38953
38954     getVMaxSize : function(){
38955          var cmax = this.config.maxSize || 10000;
38956          var center = this.mgr.getRegion("center");
38957          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38958     },
38959
38960     onSplitMove : function(split, newSize){
38961         this.fireEvent("resized", this, newSize);
38962     },
38963     
38964     /** 
38965      * Returns the {@link Roo.SplitBar} for this region.
38966      * @return {Roo.SplitBar}
38967      */
38968     getSplitBar : function(){
38969         return this.split;
38970     },
38971     
38972     hide : function(){
38973         this.hideSplitter();
38974         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38975     },
38976
38977     hideSplitter : function(){
38978         if(this.split){
38979             this.split.el.setLocation(-2000,-2000);
38980             this.split.el.hide();
38981         }
38982     },
38983
38984     show : function(){
38985         if(this.split){
38986             this.split.el.show();
38987         }
38988         Roo.bootstrap.layout.Split.superclass.show.call(this);
38989     },
38990     
38991     beforeSlide: function(){
38992         if(Roo.isGecko){// firefox overflow auto bug workaround
38993             this.bodyEl.clip();
38994             if(this.tabs) {
38995                 this.tabs.bodyEl.clip();
38996             }
38997             if(this.activePanel){
38998                 this.activePanel.getEl().clip();
38999                 
39000                 if(this.activePanel.beforeSlide){
39001                     this.activePanel.beforeSlide();
39002                 }
39003             }
39004         }
39005     },
39006     
39007     afterSlide : function(){
39008         if(Roo.isGecko){// firefox overflow auto bug workaround
39009             this.bodyEl.unclip();
39010             if(this.tabs) {
39011                 this.tabs.bodyEl.unclip();
39012             }
39013             if(this.activePanel){
39014                 this.activePanel.getEl().unclip();
39015                 if(this.activePanel.afterSlide){
39016                     this.activePanel.afterSlide();
39017                 }
39018             }
39019         }
39020     },
39021
39022     initAutoHide : function(){
39023         if(this.autoHide !== false){
39024             if(!this.autoHideHd){
39025                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39026                 this.autoHideHd = {
39027                     "mouseout": function(e){
39028                         if(!e.within(this.el, true)){
39029                             st.delay(500);
39030                         }
39031                     },
39032                     "mouseover" : function(e){
39033                         st.cancel();
39034                     },
39035                     scope : this
39036                 };
39037             }
39038             this.el.on(this.autoHideHd);
39039         }
39040     },
39041
39042     clearAutoHide : function(){
39043         if(this.autoHide !== false){
39044             this.el.un("mouseout", this.autoHideHd.mouseout);
39045             this.el.un("mouseover", this.autoHideHd.mouseover);
39046         }
39047     },
39048
39049     clearMonitor : function(){
39050         Roo.get(document).un("click", this.slideInIf, this);
39051     },
39052
39053     // these names are backwards but not changed for compat
39054     slideOut : function(){
39055         if(this.isSlid || this.el.hasActiveFx()){
39056             return;
39057         }
39058         this.isSlid = true;
39059         if(this.collapseBtn){
39060             this.collapseBtn.hide();
39061         }
39062         this.closeBtnState = this.closeBtn.getStyle('display');
39063         this.closeBtn.hide();
39064         if(this.stickBtn){
39065             this.stickBtn.show();
39066         }
39067         this.el.show();
39068         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39069         this.beforeSlide();
39070         this.el.setStyle("z-index", 10001);
39071         this.el.slideIn(this.getSlideAnchor(), {
39072             callback: function(){
39073                 this.afterSlide();
39074                 this.initAutoHide();
39075                 Roo.get(document).on("click", this.slideInIf, this);
39076                 this.fireEvent("slideshow", this);
39077             },
39078             scope: this,
39079             block: true
39080         });
39081     },
39082
39083     afterSlideIn : function(){
39084         this.clearAutoHide();
39085         this.isSlid = false;
39086         this.clearMonitor();
39087         this.el.setStyle("z-index", "");
39088         if(this.collapseBtn){
39089             this.collapseBtn.show();
39090         }
39091         this.closeBtn.setStyle('display', this.closeBtnState);
39092         if(this.stickBtn){
39093             this.stickBtn.hide();
39094         }
39095         this.fireEvent("slidehide", this);
39096     },
39097
39098     slideIn : function(cb){
39099         if(!this.isSlid || this.el.hasActiveFx()){
39100             Roo.callback(cb);
39101             return;
39102         }
39103         this.isSlid = false;
39104         this.beforeSlide();
39105         this.el.slideOut(this.getSlideAnchor(), {
39106             callback: function(){
39107                 this.el.setLeftTop(-10000, -10000);
39108                 this.afterSlide();
39109                 this.afterSlideIn();
39110                 Roo.callback(cb);
39111             },
39112             scope: this,
39113             block: true
39114         });
39115     },
39116     
39117     slideInIf : function(e){
39118         if(!e.within(this.el)){
39119             this.slideIn();
39120         }
39121     },
39122
39123     animateCollapse : function(){
39124         this.beforeSlide();
39125         this.el.setStyle("z-index", 20000);
39126         var anchor = this.getSlideAnchor();
39127         this.el.slideOut(anchor, {
39128             callback : function(){
39129                 this.el.setStyle("z-index", "");
39130                 this.collapsedEl.slideIn(anchor, {duration:.3});
39131                 this.afterSlide();
39132                 this.el.setLocation(-10000,-10000);
39133                 this.el.hide();
39134                 this.fireEvent("collapsed", this);
39135             },
39136             scope: this,
39137             block: true
39138         });
39139     },
39140
39141     animateExpand : function(){
39142         this.beforeSlide();
39143         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39144         this.el.setStyle("z-index", 20000);
39145         this.collapsedEl.hide({
39146             duration:.1
39147         });
39148         this.el.slideIn(this.getSlideAnchor(), {
39149             callback : function(){
39150                 this.el.setStyle("z-index", "");
39151                 this.afterSlide();
39152                 if(this.split){
39153                     this.split.el.show();
39154                 }
39155                 this.fireEvent("invalidated", this);
39156                 this.fireEvent("expanded", this);
39157             },
39158             scope: this,
39159             block: true
39160         });
39161     },
39162
39163     anchors : {
39164         "west" : "left",
39165         "east" : "right",
39166         "north" : "top",
39167         "south" : "bottom"
39168     },
39169
39170     sanchors : {
39171         "west" : "l",
39172         "east" : "r",
39173         "north" : "t",
39174         "south" : "b"
39175     },
39176
39177     canchors : {
39178         "west" : "tl-tr",
39179         "east" : "tr-tl",
39180         "north" : "tl-bl",
39181         "south" : "bl-tl"
39182     },
39183
39184     getAnchor : function(){
39185         return this.anchors[this.position];
39186     },
39187
39188     getCollapseAnchor : function(){
39189         return this.canchors[this.position];
39190     },
39191
39192     getSlideAnchor : function(){
39193         return this.sanchors[this.position];
39194     },
39195
39196     getAlignAdj : function(){
39197         var cm = this.cmargins;
39198         switch(this.position){
39199             case "west":
39200                 return [0, 0];
39201             break;
39202             case "east":
39203                 return [0, 0];
39204             break;
39205             case "north":
39206                 return [0, 0];
39207             break;
39208             case "south":
39209                 return [0, 0];
39210             break;
39211         }
39212     },
39213
39214     getExpandAdj : function(){
39215         var c = this.collapsedEl, cm = this.cmargins;
39216         switch(this.position){
39217             case "west":
39218                 return [-(cm.right+c.getWidth()+cm.left), 0];
39219             break;
39220             case "east":
39221                 return [cm.right+c.getWidth()+cm.left, 0];
39222             break;
39223             case "north":
39224                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39225             break;
39226             case "south":
39227                 return [0, cm.top+cm.bottom+c.getHeight()];
39228             break;
39229         }
39230     }
39231 });/*
39232  * Based on:
39233  * Ext JS Library 1.1.1
39234  * Copyright(c) 2006-2007, Ext JS, LLC.
39235  *
39236  * Originally Released Under LGPL - original licence link has changed is not relivant.
39237  *
39238  * Fork - LGPL
39239  * <script type="text/javascript">
39240  */
39241 /*
39242  * These classes are private internal classes
39243  */
39244 Roo.bootstrap.layout.Center = function(config){
39245     config.region = "center";
39246     Roo.bootstrap.layout.Region.call(this, config);
39247     this.visible = true;
39248     this.minWidth = config.minWidth || 20;
39249     this.minHeight = config.minHeight || 20;
39250 };
39251
39252 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39253     hide : function(){
39254         // center panel can't be hidden
39255     },
39256     
39257     show : function(){
39258         // center panel can't be hidden
39259     },
39260     
39261     getMinWidth: function(){
39262         return this.minWidth;
39263     },
39264     
39265     getMinHeight: function(){
39266         return this.minHeight;
39267     }
39268 });
39269
39270
39271
39272
39273  
39274
39275
39276
39277
39278
39279
39280 Roo.bootstrap.layout.North = function(config)
39281 {
39282     config.region = 'north';
39283     config.cursor = 'n-resize';
39284     
39285     Roo.bootstrap.layout.Split.call(this, config);
39286     
39287     
39288     if(this.split){
39289         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39290         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39291         this.split.el.addClass("roo-layout-split-v");
39292     }
39293     //var size = config.initialSize || config.height;
39294     //if(this.el && typeof size != "undefined"){
39295     //    this.el.setHeight(size);
39296     //}
39297 };
39298 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39299 {
39300     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39301      
39302      
39303     onRender : function(ctr, pos)
39304     {
39305         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39306         var size = this.config.initialSize || this.config.height;
39307         if(this.el && typeof size != "undefined"){
39308             this.el.setHeight(size);
39309         }
39310     
39311     },
39312     
39313     getBox : function(){
39314         if(this.collapsed){
39315             return this.collapsedEl.getBox();
39316         }
39317         var box = this.el.getBox();
39318         if(this.split){
39319             box.height += this.split.el.getHeight();
39320         }
39321         return box;
39322     },
39323     
39324     updateBox : function(box){
39325         if(this.split && !this.collapsed){
39326             box.height -= this.split.el.getHeight();
39327             this.split.el.setLeft(box.x);
39328             this.split.el.setTop(box.y+box.height);
39329             this.split.el.setWidth(box.width);
39330         }
39331         if(this.collapsed){
39332             this.updateBody(box.width, null);
39333         }
39334         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39335     }
39336 });
39337
39338
39339
39340
39341
39342 Roo.bootstrap.layout.South = function(config){
39343     config.region = 'south';
39344     config.cursor = 's-resize';
39345     Roo.bootstrap.layout.Split.call(this, config);
39346     if(this.split){
39347         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39348         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39349         this.split.el.addClass("roo-layout-split-v");
39350     }
39351     
39352 };
39353
39354 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39355     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39356     
39357     onRender : function(ctr, pos)
39358     {
39359         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39360         var size = this.config.initialSize || this.config.height;
39361         if(this.el && typeof size != "undefined"){
39362             this.el.setHeight(size);
39363         }
39364     
39365     },
39366     
39367     getBox : function(){
39368         if(this.collapsed){
39369             return this.collapsedEl.getBox();
39370         }
39371         var box = this.el.getBox();
39372         if(this.split){
39373             var sh = this.split.el.getHeight();
39374             box.height += sh;
39375             box.y -= sh;
39376         }
39377         return box;
39378     },
39379     
39380     updateBox : function(box){
39381         if(this.split && !this.collapsed){
39382             var sh = this.split.el.getHeight();
39383             box.height -= sh;
39384             box.y += sh;
39385             this.split.el.setLeft(box.x);
39386             this.split.el.setTop(box.y-sh);
39387             this.split.el.setWidth(box.width);
39388         }
39389         if(this.collapsed){
39390             this.updateBody(box.width, null);
39391         }
39392         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39393     }
39394 });
39395
39396 Roo.bootstrap.layout.East = function(config){
39397     config.region = "east";
39398     config.cursor = "e-resize";
39399     Roo.bootstrap.layout.Split.call(this, config);
39400     if(this.split){
39401         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39402         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39403         this.split.el.addClass("roo-layout-split-h");
39404     }
39405     
39406 };
39407 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39408     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39409     
39410     onRender : function(ctr, pos)
39411     {
39412         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39413         var size = this.config.initialSize || this.config.width;
39414         if(this.el && typeof size != "undefined"){
39415             this.el.setWidth(size);
39416         }
39417     
39418     },
39419     
39420     getBox : function(){
39421         if(this.collapsed){
39422             return this.collapsedEl.getBox();
39423         }
39424         var box = this.el.getBox();
39425         if(this.split){
39426             var sw = this.split.el.getWidth();
39427             box.width += sw;
39428             box.x -= sw;
39429         }
39430         return box;
39431     },
39432
39433     updateBox : function(box){
39434         if(this.split && !this.collapsed){
39435             var sw = this.split.el.getWidth();
39436             box.width -= sw;
39437             this.split.el.setLeft(box.x);
39438             this.split.el.setTop(box.y);
39439             this.split.el.setHeight(box.height);
39440             box.x += sw;
39441         }
39442         if(this.collapsed){
39443             this.updateBody(null, box.height);
39444         }
39445         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39446     }
39447 });
39448
39449 Roo.bootstrap.layout.West = function(config){
39450     config.region = "west";
39451     config.cursor = "w-resize";
39452     
39453     Roo.bootstrap.layout.Split.call(this, config);
39454     if(this.split){
39455         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39456         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39457         this.split.el.addClass("roo-layout-split-h");
39458     }
39459     
39460 };
39461 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39462     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39463     
39464     onRender: function(ctr, pos)
39465     {
39466         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39467         var size = this.config.initialSize || this.config.width;
39468         if(typeof size != "undefined"){
39469             this.el.setWidth(size);
39470         }
39471     },
39472     
39473     getBox : function(){
39474         if(this.collapsed){
39475             return this.collapsedEl.getBox();
39476         }
39477         var box = this.el.getBox();
39478         if (box.width == 0) {
39479             box.width = this.config.width; // kludge?
39480         }
39481         if(this.split){
39482             box.width += this.split.el.getWidth();
39483         }
39484         return box;
39485     },
39486     
39487     updateBox : function(box){
39488         if(this.split && !this.collapsed){
39489             var sw = this.split.el.getWidth();
39490             box.width -= sw;
39491             this.split.el.setLeft(box.x+box.width);
39492             this.split.el.setTop(box.y);
39493             this.split.el.setHeight(box.height);
39494         }
39495         if(this.collapsed){
39496             this.updateBody(null, box.height);
39497         }
39498         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39499     }
39500 });Roo.namespace("Roo.bootstrap.panel");/*
39501  * Based on:
39502  * Ext JS Library 1.1.1
39503  * Copyright(c) 2006-2007, Ext JS, LLC.
39504  *
39505  * Originally Released Under LGPL - original licence link has changed is not relivant.
39506  *
39507  * Fork - LGPL
39508  * <script type="text/javascript">
39509  */
39510 /**
39511  * @class Roo.ContentPanel
39512  * @extends Roo.util.Observable
39513  * A basic ContentPanel element.
39514  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39515  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39516  * @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
39517  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39518  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39519  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39520  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39521  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39522  * @cfg {String} title          The title for this panel
39523  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39524  * @cfg {String} url            Calls {@link #setUrl} with this value
39525  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39526  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39527  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39528  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39529  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39530  * @cfg {Boolean} badges render the badges
39531  * @cfg {String} cls  extra classes to use  
39532  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39533
39534  * @constructor
39535  * Create a new ContentPanel.
39536  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39537  * @param {String/Object} config A string to set only the title or a config object
39538  * @param {String} content (optional) Set the HTML content for this panel
39539  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39540  */
39541 Roo.bootstrap.panel.Content = function( config){
39542     
39543     this.tpl = config.tpl || false;
39544     
39545     var el = config.el;
39546     var content = config.content;
39547
39548     if(config.autoCreate){ // xtype is available if this is called from factory
39549         el = Roo.id();
39550     }
39551     this.el = Roo.get(el);
39552     if(!this.el && config && config.autoCreate){
39553         if(typeof config.autoCreate == "object"){
39554             if(!config.autoCreate.id){
39555                 config.autoCreate.id = config.id||el;
39556             }
39557             this.el = Roo.DomHelper.append(document.body,
39558                         config.autoCreate, true);
39559         }else{
39560             var elcfg =  {
39561                 tag: "div",
39562                 cls: (config.cls || '') +
39563                     (config.background ? ' bg-' + config.background : '') +
39564                     " roo-layout-inactive-content",
39565                 id: config.id||el
39566             };
39567             if (config.iframe) {
39568                 elcfg.cn = [
39569                     {
39570                         tag : 'iframe',
39571                         style : 'border: 0px',
39572                         src : 'about:blank'
39573                     }
39574                 ];
39575             }
39576               
39577             if (config.html) {
39578                 elcfg.html = config.html;
39579                 
39580             }
39581                         
39582             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39583             if (config.iframe) {
39584                 this.iframeEl = this.el.select('iframe',true).first();
39585             }
39586             
39587         }
39588     } 
39589     this.closable = false;
39590     this.loaded = false;
39591     this.active = false;
39592    
39593       
39594     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39595         
39596         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39597         
39598         this.wrapEl = this.el; //this.el.wrap();
39599         var ti = [];
39600         if (config.toolbar.items) {
39601             ti = config.toolbar.items ;
39602             delete config.toolbar.items ;
39603         }
39604         
39605         var nitems = [];
39606         this.toolbar.render(this.wrapEl, 'before');
39607         for(var i =0;i < ti.length;i++) {
39608           //  Roo.log(['add child', items[i]]);
39609             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39610         }
39611         this.toolbar.items = nitems;
39612         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39613         delete config.toolbar;
39614         
39615     }
39616     /*
39617     // xtype created footer. - not sure if will work as we normally have to render first..
39618     if (this.footer && !this.footer.el && this.footer.xtype) {
39619         if (!this.wrapEl) {
39620             this.wrapEl = this.el.wrap();
39621         }
39622     
39623         this.footer.container = this.wrapEl.createChild();
39624          
39625         this.footer = Roo.factory(this.footer, Roo);
39626         
39627     }
39628     */
39629     
39630      if(typeof config == "string"){
39631         this.title = config;
39632     }else{
39633         Roo.apply(this, config);
39634     }
39635     
39636     if(this.resizeEl){
39637         this.resizeEl = Roo.get(this.resizeEl, true);
39638     }else{
39639         this.resizeEl = this.el;
39640     }
39641     // handle view.xtype
39642     
39643  
39644     
39645     
39646     this.addEvents({
39647         /**
39648          * @event activate
39649          * Fires when this panel is activated. 
39650          * @param {Roo.ContentPanel} this
39651          */
39652         "activate" : true,
39653         /**
39654          * @event deactivate
39655          * Fires when this panel is activated. 
39656          * @param {Roo.ContentPanel} this
39657          */
39658         "deactivate" : true,
39659
39660         /**
39661          * @event resize
39662          * Fires when this panel is resized if fitToFrame is true.
39663          * @param {Roo.ContentPanel} this
39664          * @param {Number} width The width after any component adjustments
39665          * @param {Number} height The height after any component adjustments
39666          */
39667         "resize" : true,
39668         
39669          /**
39670          * @event render
39671          * Fires when this tab is created
39672          * @param {Roo.ContentPanel} this
39673          */
39674         "render" : true
39675         
39676         
39677         
39678     });
39679     
39680
39681     
39682     
39683     if(this.autoScroll && !this.iframe){
39684         this.resizeEl.setStyle("overflow", "auto");
39685     } else {
39686         // fix randome scrolling
39687         //this.el.on('scroll', function() {
39688         //    Roo.log('fix random scolling');
39689         //    this.scrollTo('top',0); 
39690         //});
39691     }
39692     content = content || this.content;
39693     if(content){
39694         this.setContent(content);
39695     }
39696     if(config && config.url){
39697         this.setUrl(this.url, this.params, this.loadOnce);
39698     }
39699     
39700     
39701     
39702     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39703     
39704     if (this.view && typeof(this.view.xtype) != 'undefined') {
39705         this.view.el = this.el.appendChild(document.createElement("div"));
39706         this.view = Roo.factory(this.view); 
39707         this.view.render  &&  this.view.render(false, '');  
39708     }
39709     
39710     
39711     this.fireEvent('render', this);
39712 };
39713
39714 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39715     
39716     cls : '',
39717     background : '',
39718     
39719     tabTip : '',
39720     
39721     iframe : false,
39722     iframeEl : false,
39723     
39724     setRegion : function(region){
39725         this.region = region;
39726         this.setActiveClass(region && !this.background);
39727     },
39728     
39729     
39730     setActiveClass: function(state)
39731     {
39732         if(state){
39733            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39734            this.el.setStyle('position','relative');
39735         }else{
39736            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39737            this.el.setStyle('position', 'absolute');
39738         } 
39739     },
39740     
39741     /**
39742      * Returns the toolbar for this Panel if one was configured. 
39743      * @return {Roo.Toolbar} 
39744      */
39745     getToolbar : function(){
39746         return this.toolbar;
39747     },
39748     
39749     setActiveState : function(active)
39750     {
39751         this.active = active;
39752         this.setActiveClass(active);
39753         if(!active){
39754             if(this.fireEvent("deactivate", this) === false){
39755                 return false;
39756             }
39757             return true;
39758         }
39759         this.fireEvent("activate", this);
39760         return true;
39761     },
39762     /**
39763      * Updates this panel's element (not for iframe)
39764      * @param {String} content The new content
39765      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39766     */
39767     setContent : function(content, loadScripts){
39768         if (this.iframe) {
39769             return;
39770         }
39771         
39772         this.el.update(content, loadScripts);
39773     },
39774
39775     ignoreResize : function(w, h){
39776         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39777             return true;
39778         }else{
39779             this.lastSize = {width: w, height: h};
39780             return false;
39781         }
39782     },
39783     /**
39784      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39785      * @return {Roo.UpdateManager} The UpdateManager
39786      */
39787     getUpdateManager : function(){
39788         if (this.iframe) {
39789             return false;
39790         }
39791         return this.el.getUpdateManager();
39792     },
39793      /**
39794      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39795      * Does not work with IFRAME contents
39796      * @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:
39797 <pre><code>
39798 panel.load({
39799     url: "your-url.php",
39800     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39801     callback: yourFunction,
39802     scope: yourObject, //(optional scope)
39803     discardUrl: false,
39804     nocache: false,
39805     text: "Loading...",
39806     timeout: 30,
39807     scripts: false
39808 });
39809 </code></pre>
39810      
39811      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39812      * 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.
39813      * @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}
39814      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39815      * @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.
39816      * @return {Roo.ContentPanel} this
39817      */
39818     load : function(){
39819         
39820         if (this.iframe) {
39821             return this;
39822         }
39823         
39824         var um = this.el.getUpdateManager();
39825         um.update.apply(um, arguments);
39826         return this;
39827     },
39828
39829
39830     /**
39831      * 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.
39832      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39833      * @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)
39834      * @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)
39835      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39836      */
39837     setUrl : function(url, params, loadOnce){
39838         if (this.iframe) {
39839             this.iframeEl.dom.src = url;
39840             return false;
39841         }
39842         
39843         if(this.refreshDelegate){
39844             this.removeListener("activate", this.refreshDelegate);
39845         }
39846         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39847         this.on("activate", this.refreshDelegate);
39848         return this.el.getUpdateManager();
39849     },
39850     
39851     _handleRefresh : function(url, params, loadOnce){
39852         if(!loadOnce || !this.loaded){
39853             var updater = this.el.getUpdateManager();
39854             updater.update(url, params, this._setLoaded.createDelegate(this));
39855         }
39856     },
39857     
39858     _setLoaded : function(){
39859         this.loaded = true;
39860     }, 
39861     
39862     /**
39863      * Returns this panel's id
39864      * @return {String} 
39865      */
39866     getId : function(){
39867         return this.el.id;
39868     },
39869     
39870     /** 
39871      * Returns this panel's element - used by regiosn to add.
39872      * @return {Roo.Element} 
39873      */
39874     getEl : function(){
39875         return this.wrapEl || this.el;
39876     },
39877     
39878    
39879     
39880     adjustForComponents : function(width, height)
39881     {
39882         //Roo.log('adjustForComponents ');
39883         if(this.resizeEl != this.el){
39884             width -= this.el.getFrameWidth('lr');
39885             height -= this.el.getFrameWidth('tb');
39886         }
39887         if(this.toolbar){
39888             var te = this.toolbar.getEl();
39889             te.setWidth(width);
39890             height -= te.getHeight();
39891         }
39892         if(this.footer){
39893             var te = this.footer.getEl();
39894             te.setWidth(width);
39895             height -= te.getHeight();
39896         }
39897         
39898         
39899         if(this.adjustments){
39900             width += this.adjustments[0];
39901             height += this.adjustments[1];
39902         }
39903         return {"width": width, "height": height};
39904     },
39905     
39906     setSize : function(width, height){
39907         if(this.fitToFrame && !this.ignoreResize(width, height)){
39908             if(this.fitContainer && this.resizeEl != this.el){
39909                 this.el.setSize(width, height);
39910             }
39911             var size = this.adjustForComponents(width, height);
39912             if (this.iframe) {
39913                 this.iframeEl.setSize(width,height);
39914             }
39915             
39916             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39917             this.fireEvent('resize', this, size.width, size.height);
39918             
39919             
39920         }
39921     },
39922     
39923     /**
39924      * Returns this panel's title
39925      * @return {String} 
39926      */
39927     getTitle : function(){
39928         
39929         if (typeof(this.title) != 'object') {
39930             return this.title;
39931         }
39932         
39933         var t = '';
39934         for (var k in this.title) {
39935             if (!this.title.hasOwnProperty(k)) {
39936                 continue;
39937             }
39938             
39939             if (k.indexOf('-') >= 0) {
39940                 var s = k.split('-');
39941                 for (var i = 0; i<s.length; i++) {
39942                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39943                 }
39944             } else {
39945                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39946             }
39947         }
39948         return t;
39949     },
39950     
39951     /**
39952      * Set this panel's title
39953      * @param {String} title
39954      */
39955     setTitle : function(title){
39956         this.title = title;
39957         if(this.region){
39958             this.region.updatePanelTitle(this, title);
39959         }
39960     },
39961     
39962     /**
39963      * Returns true is this panel was configured to be closable
39964      * @return {Boolean} 
39965      */
39966     isClosable : function(){
39967         return this.closable;
39968     },
39969     
39970     beforeSlide : function(){
39971         this.el.clip();
39972         this.resizeEl.clip();
39973     },
39974     
39975     afterSlide : function(){
39976         this.el.unclip();
39977         this.resizeEl.unclip();
39978     },
39979     
39980     /**
39981      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39982      *   Will fail silently if the {@link #setUrl} method has not been called.
39983      *   This does not activate the panel, just updates its content.
39984      */
39985     refresh : function(){
39986         if(this.refreshDelegate){
39987            this.loaded = false;
39988            this.refreshDelegate();
39989         }
39990     },
39991     
39992     /**
39993      * Destroys this panel
39994      */
39995     destroy : function(){
39996         this.el.removeAllListeners();
39997         var tempEl = document.createElement("span");
39998         tempEl.appendChild(this.el.dom);
39999         tempEl.innerHTML = "";
40000         this.el.remove();
40001         this.el = null;
40002     },
40003     
40004     /**
40005      * form - if the content panel contains a form - this is a reference to it.
40006      * @type {Roo.form.Form}
40007      */
40008     form : false,
40009     /**
40010      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40011      *    This contains a reference to it.
40012      * @type {Roo.View}
40013      */
40014     view : false,
40015     
40016       /**
40017      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40018      * <pre><code>
40019
40020 layout.addxtype({
40021        xtype : 'Form',
40022        items: [ .... ]
40023    }
40024 );
40025
40026 </code></pre>
40027      * @param {Object} cfg Xtype definition of item to add.
40028      */
40029     
40030     
40031     getChildContainer: function () {
40032         return this.getEl();
40033     }
40034     
40035     
40036     /*
40037         var  ret = new Roo.factory(cfg);
40038         return ret;
40039         
40040         
40041         // add form..
40042         if (cfg.xtype.match(/^Form$/)) {
40043             
40044             var el;
40045             //if (this.footer) {
40046             //    el = this.footer.container.insertSibling(false, 'before');
40047             //} else {
40048                 el = this.el.createChild();
40049             //}
40050
40051             this.form = new  Roo.form.Form(cfg);
40052             
40053             
40054             if ( this.form.allItems.length) {
40055                 this.form.render(el.dom);
40056             }
40057             return this.form;
40058         }
40059         // should only have one of theses..
40060         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40061             // views.. should not be just added - used named prop 'view''
40062             
40063             cfg.el = this.el.appendChild(document.createElement("div"));
40064             // factory?
40065             
40066             var ret = new Roo.factory(cfg);
40067              
40068              ret.render && ret.render(false, ''); // render blank..
40069             this.view = ret;
40070             return ret;
40071         }
40072         return false;
40073     }
40074     \*/
40075 });
40076  
40077 /**
40078  * @class Roo.bootstrap.panel.Grid
40079  * @extends Roo.bootstrap.panel.Content
40080  * @constructor
40081  * Create a new GridPanel.
40082  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40083  * @param {Object} config A the config object
40084   
40085  */
40086
40087
40088
40089 Roo.bootstrap.panel.Grid = function(config)
40090 {
40091     
40092       
40093     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40094         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40095
40096     config.el = this.wrapper;
40097     //this.el = this.wrapper;
40098     
40099       if (config.container) {
40100         // ctor'ed from a Border/panel.grid
40101         
40102         
40103         this.wrapper.setStyle("overflow", "hidden");
40104         this.wrapper.addClass('roo-grid-container');
40105
40106     }
40107     
40108     
40109     if(config.toolbar){
40110         var tool_el = this.wrapper.createChild();    
40111         this.toolbar = Roo.factory(config.toolbar);
40112         var ti = [];
40113         if (config.toolbar.items) {
40114             ti = config.toolbar.items ;
40115             delete config.toolbar.items ;
40116         }
40117         
40118         var nitems = [];
40119         this.toolbar.render(tool_el);
40120         for(var i =0;i < ti.length;i++) {
40121           //  Roo.log(['add child', items[i]]);
40122             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40123         }
40124         this.toolbar.items = nitems;
40125         
40126         delete config.toolbar;
40127     }
40128     
40129     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40130     config.grid.scrollBody = true;;
40131     config.grid.monitorWindowResize = false; // turn off autosizing
40132     config.grid.autoHeight = false;
40133     config.grid.autoWidth = false;
40134     
40135     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40136     
40137     if (config.background) {
40138         // render grid on panel activation (if panel background)
40139         this.on('activate', function(gp) {
40140             if (!gp.grid.rendered) {
40141                 gp.grid.render(this.wrapper);
40142                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40143             }
40144         });
40145             
40146     } else {
40147         this.grid.render(this.wrapper);
40148         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40149
40150     }
40151     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40152     // ??? needed ??? config.el = this.wrapper;
40153     
40154     
40155     
40156   
40157     // xtype created footer. - not sure if will work as we normally have to render first..
40158     if (this.footer && !this.footer.el && this.footer.xtype) {
40159         
40160         var ctr = this.grid.getView().getFooterPanel(true);
40161         this.footer.dataSource = this.grid.dataSource;
40162         this.footer = Roo.factory(this.footer, Roo);
40163         this.footer.render(ctr);
40164         
40165     }
40166     
40167     
40168     
40169     
40170      
40171 };
40172
40173 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40174     getId : function(){
40175         return this.grid.id;
40176     },
40177     
40178     /**
40179      * Returns the grid for this panel
40180      * @return {Roo.bootstrap.Table} 
40181      */
40182     getGrid : function(){
40183         return this.grid;    
40184     },
40185     
40186     setSize : function(width, height){
40187         if(!this.ignoreResize(width, height)){
40188             var grid = this.grid;
40189             var size = this.adjustForComponents(width, height);
40190             // tfoot is not a footer?
40191           
40192             
40193             var gridel = grid.getGridEl();
40194             gridel.setSize(size.width, size.height);
40195             
40196             var tbd = grid.getGridEl().select('tbody', true).first();
40197             var thd = grid.getGridEl().select('thead',true).first();
40198             var tbf= grid.getGridEl().select('tfoot', true).first();
40199
40200             if (tbf) {
40201                 size.height -= tbf.getHeight();
40202             }
40203             if (thd) {
40204                 size.height -= thd.getHeight();
40205             }
40206             
40207             tbd.setSize(size.width, size.height );
40208             // this is for the account management tab -seems to work there.
40209             var thd = grid.getGridEl().select('thead',true).first();
40210             //if (tbd) {
40211             //    tbd.setSize(size.width, size.height - thd.getHeight());
40212             //}
40213              
40214             grid.autoSize();
40215         }
40216     },
40217      
40218     
40219     
40220     beforeSlide : function(){
40221         this.grid.getView().scroller.clip();
40222     },
40223     
40224     afterSlide : function(){
40225         this.grid.getView().scroller.unclip();
40226     },
40227     
40228     destroy : function(){
40229         this.grid.destroy();
40230         delete this.grid;
40231         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40232     }
40233 });
40234
40235 /**
40236  * @class Roo.bootstrap.panel.Nest
40237  * @extends Roo.bootstrap.panel.Content
40238  * @constructor
40239  * Create a new Panel, that can contain a layout.Border.
40240  * 
40241  * 
40242  * @param {Roo.BorderLayout} layout The layout for this panel
40243  * @param {String/Object} config A string to set only the title or a config object
40244  */
40245 Roo.bootstrap.panel.Nest = function(config)
40246 {
40247     // construct with only one argument..
40248     /* FIXME - implement nicer consturctors
40249     if (layout.layout) {
40250         config = layout;
40251         layout = config.layout;
40252         delete config.layout;
40253     }
40254     if (layout.xtype && !layout.getEl) {
40255         // then layout needs constructing..
40256         layout = Roo.factory(layout, Roo);
40257     }
40258     */
40259     
40260     config.el =  config.layout.getEl();
40261     
40262     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40263     
40264     config.layout.monitorWindowResize = false; // turn off autosizing
40265     this.layout = config.layout;
40266     this.layout.getEl().addClass("roo-layout-nested-layout");
40267     this.layout.parent = this;
40268     
40269     
40270     
40271     
40272 };
40273
40274 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40275
40276     setSize : function(width, height){
40277         if(!this.ignoreResize(width, height)){
40278             var size = this.adjustForComponents(width, height);
40279             var el = this.layout.getEl();
40280             if (size.height < 1) {
40281                 el.setWidth(size.width);   
40282             } else {
40283                 el.setSize(size.width, size.height);
40284             }
40285             var touch = el.dom.offsetWidth;
40286             this.layout.layout();
40287             // ie requires a double layout on the first pass
40288             if(Roo.isIE && !this.initialized){
40289                 this.initialized = true;
40290                 this.layout.layout();
40291             }
40292         }
40293     },
40294     
40295     // activate all subpanels if not currently active..
40296     
40297     setActiveState : function(active){
40298         this.active = active;
40299         this.setActiveClass(active);
40300         
40301         if(!active){
40302             this.fireEvent("deactivate", this);
40303             return;
40304         }
40305         
40306         this.fireEvent("activate", this);
40307         // not sure if this should happen before or after..
40308         if (!this.layout) {
40309             return; // should not happen..
40310         }
40311         var reg = false;
40312         for (var r in this.layout.regions) {
40313             reg = this.layout.getRegion(r);
40314             if (reg.getActivePanel()) {
40315                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40316                 reg.setActivePanel(reg.getActivePanel());
40317                 continue;
40318             }
40319             if (!reg.panels.length) {
40320                 continue;
40321             }
40322             reg.showPanel(reg.getPanel(0));
40323         }
40324         
40325         
40326         
40327         
40328     },
40329     
40330     /**
40331      * Returns the nested BorderLayout for this panel
40332      * @return {Roo.BorderLayout} 
40333      */
40334     getLayout : function(){
40335         return this.layout;
40336     },
40337     
40338      /**
40339      * Adds a xtype elements to the layout of the nested panel
40340      * <pre><code>
40341
40342 panel.addxtype({
40343        xtype : 'ContentPanel',
40344        region: 'west',
40345        items: [ .... ]
40346    }
40347 );
40348
40349 panel.addxtype({
40350         xtype : 'NestedLayoutPanel',
40351         region: 'west',
40352         layout: {
40353            center: { },
40354            west: { }   
40355         },
40356         items : [ ... list of content panels or nested layout panels.. ]
40357    }
40358 );
40359 </code></pre>
40360      * @param {Object} cfg Xtype definition of item to add.
40361      */
40362     addxtype : function(cfg) {
40363         return this.layout.addxtype(cfg);
40364     
40365     }
40366 });/*
40367  * Based on:
40368  * Ext JS Library 1.1.1
40369  * Copyright(c) 2006-2007, Ext JS, LLC.
40370  *
40371  * Originally Released Under LGPL - original licence link has changed is not relivant.
40372  *
40373  * Fork - LGPL
40374  * <script type="text/javascript">
40375  */
40376 /**
40377  * @class Roo.TabPanel
40378  * @extends Roo.util.Observable
40379  * A lightweight tab container.
40380  * <br><br>
40381  * Usage:
40382  * <pre><code>
40383 // basic tabs 1, built from existing content
40384 var tabs = new Roo.TabPanel("tabs1");
40385 tabs.addTab("script", "View Script");
40386 tabs.addTab("markup", "View Markup");
40387 tabs.activate("script");
40388
40389 // more advanced tabs, built from javascript
40390 var jtabs = new Roo.TabPanel("jtabs");
40391 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40392
40393 // set up the UpdateManager
40394 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40395 var updater = tab2.getUpdateManager();
40396 updater.setDefaultUrl("ajax1.htm");
40397 tab2.on('activate', updater.refresh, updater, true);
40398
40399 // Use setUrl for Ajax loading
40400 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40401 tab3.setUrl("ajax2.htm", null, true);
40402
40403 // Disabled tab
40404 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40405 tab4.disable();
40406
40407 jtabs.activate("jtabs-1");
40408  * </code></pre>
40409  * @constructor
40410  * Create a new TabPanel.
40411  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40412  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40413  */
40414 Roo.bootstrap.panel.Tabs = function(config){
40415     /**
40416     * The container element for this TabPanel.
40417     * @type Roo.Element
40418     */
40419     this.el = Roo.get(config.el);
40420     delete config.el;
40421     if(config){
40422         if(typeof config == "boolean"){
40423             this.tabPosition = config ? "bottom" : "top";
40424         }else{
40425             Roo.apply(this, config);
40426         }
40427     }
40428     
40429     if(this.tabPosition == "bottom"){
40430         // if tabs are at the bottom = create the body first.
40431         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40432         this.el.addClass("roo-tabs-bottom");
40433     }
40434     // next create the tabs holders
40435     
40436     if (this.tabPosition == "west"){
40437         
40438         var reg = this.region; // fake it..
40439         while (reg) {
40440             if (!reg.mgr.parent) {
40441                 break;
40442             }
40443             reg = reg.mgr.parent.region;
40444         }
40445         Roo.log("got nest?");
40446         Roo.log(reg);
40447         if (reg.mgr.getRegion('west')) {
40448             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40449             this.stripWrap = Roo.get(this.createStrip(ctrdom ), 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         
40457         
40458     } else {
40459      
40460         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40461         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40462         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40463         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40464     }
40465     
40466     
40467     if(Roo.isIE){
40468         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40469     }
40470     
40471     // finally - if tabs are at the top, then create the body last..
40472     if(this.tabPosition != "bottom"){
40473         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40474          * @type Roo.Element
40475          */
40476         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40477         this.el.addClass("roo-tabs-top");
40478     }
40479     this.items = [];
40480
40481     this.bodyEl.setStyle("position", "relative");
40482
40483     this.active = null;
40484     this.activateDelegate = this.activate.createDelegate(this);
40485
40486     this.addEvents({
40487         /**
40488          * @event tabchange
40489          * Fires when the active tab changes
40490          * @param {Roo.TabPanel} this
40491          * @param {Roo.TabPanelItem} activePanel The new active tab
40492          */
40493         "tabchange": true,
40494         /**
40495          * @event beforetabchange
40496          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40497          * @param {Roo.TabPanel} this
40498          * @param {Object} e Set cancel to true on this object to cancel the tab change
40499          * @param {Roo.TabPanelItem} tab The tab being changed to
40500          */
40501         "beforetabchange" : true
40502     });
40503
40504     Roo.EventManager.onWindowResize(this.onResize, this);
40505     this.cpad = this.el.getPadding("lr");
40506     this.hiddenCount = 0;
40507
40508
40509     // toolbar on the tabbar support...
40510     if (this.toolbar) {
40511         alert("no toolbar support yet");
40512         this.toolbar  = false;
40513         /*
40514         var tcfg = this.toolbar;
40515         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40516         this.toolbar = new Roo.Toolbar(tcfg);
40517         if (Roo.isSafari) {
40518             var tbl = tcfg.container.child('table', true);
40519             tbl.setAttribute('width', '100%');
40520         }
40521         */
40522         
40523     }
40524    
40525
40526
40527     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40528 };
40529
40530 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40531     /*
40532      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40533      */
40534     tabPosition : "top",
40535     /*
40536      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40537      */
40538     currentTabWidth : 0,
40539     /*
40540      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40541      */
40542     minTabWidth : 40,
40543     /*
40544      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40545      */
40546     maxTabWidth : 250,
40547     /*
40548      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40549      */
40550     preferredTabWidth : 175,
40551     /*
40552      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40553      */
40554     resizeTabs : false,
40555     /*
40556      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40557      */
40558     monitorResize : true,
40559     /*
40560      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40561      */
40562     toolbar : false,  // set by caller..
40563     
40564     region : false, /// set by caller
40565     
40566     disableTooltips : true, // not used yet...
40567
40568     /**
40569      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40570      * @param {String} id The id of the div to use <b>or create</b>
40571      * @param {String} text The text for the tab
40572      * @param {String} content (optional) Content to put in the TabPanelItem body
40573      * @param {Boolean} closable (optional) True to create a close icon on the tab
40574      * @return {Roo.TabPanelItem} The created TabPanelItem
40575      */
40576     addTab : function(id, text, content, closable, tpl)
40577     {
40578         var item = new Roo.bootstrap.panel.TabItem({
40579             panel: this,
40580             id : id,
40581             text : text,
40582             closable : closable,
40583             tpl : tpl
40584         });
40585         this.addTabItem(item);
40586         if(content){
40587             item.setContent(content);
40588         }
40589         return item;
40590     },
40591
40592     /**
40593      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40594      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40595      * @return {Roo.TabPanelItem}
40596      */
40597     getTab : function(id){
40598         return this.items[id];
40599     },
40600
40601     /**
40602      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40603      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40604      */
40605     hideTab : function(id){
40606         var t = this.items[id];
40607         if(!t.isHidden()){
40608            t.setHidden(true);
40609            this.hiddenCount++;
40610            this.autoSizeTabs();
40611         }
40612     },
40613
40614     /**
40615      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40616      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40617      */
40618     unhideTab : function(id){
40619         var t = this.items[id];
40620         if(t.isHidden()){
40621            t.setHidden(false);
40622            this.hiddenCount--;
40623            this.autoSizeTabs();
40624         }
40625     },
40626
40627     /**
40628      * Adds an existing {@link Roo.TabPanelItem}.
40629      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40630      */
40631     addTabItem : function(item)
40632     {
40633         this.items[item.id] = item;
40634         this.items.push(item);
40635         this.autoSizeTabs();
40636       //  if(this.resizeTabs){
40637     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40638   //         this.autoSizeTabs();
40639 //        }else{
40640 //            item.autoSize();
40641        // }
40642     },
40643
40644     /**
40645      * Removes a {@link Roo.TabPanelItem}.
40646      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40647      */
40648     removeTab : function(id){
40649         var items = this.items;
40650         var tab = items[id];
40651         if(!tab) { return; }
40652         var index = items.indexOf(tab);
40653         if(this.active == tab && items.length > 1){
40654             var newTab = this.getNextAvailable(index);
40655             if(newTab) {
40656                 newTab.activate();
40657             }
40658         }
40659         this.stripEl.dom.removeChild(tab.pnode.dom);
40660         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40661             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40662         }
40663         items.splice(index, 1);
40664         delete this.items[tab.id];
40665         tab.fireEvent("close", tab);
40666         tab.purgeListeners();
40667         this.autoSizeTabs();
40668     },
40669
40670     getNextAvailable : function(start){
40671         var items = this.items;
40672         var index = start;
40673         // look for a next tab that will slide over to
40674         // replace the one being removed
40675         while(index < items.length){
40676             var item = items[++index];
40677             if(item && !item.isHidden()){
40678                 return item;
40679             }
40680         }
40681         // if one isn't found select the previous tab (on the left)
40682         index = start;
40683         while(index >= 0){
40684             var item = items[--index];
40685             if(item && !item.isHidden()){
40686                 return item;
40687             }
40688         }
40689         return null;
40690     },
40691
40692     /**
40693      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40694      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40695      */
40696     disableTab : function(id){
40697         var tab = this.items[id];
40698         if(tab && this.active != tab){
40699             tab.disable();
40700         }
40701     },
40702
40703     /**
40704      * Enables a {@link Roo.TabPanelItem} that is disabled.
40705      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40706      */
40707     enableTab : function(id){
40708         var tab = this.items[id];
40709         tab.enable();
40710     },
40711
40712     /**
40713      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40714      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40715      * @return {Roo.TabPanelItem} The TabPanelItem.
40716      */
40717     activate : function(id)
40718     {
40719         //Roo.log('activite:'  + id);
40720         
40721         var tab = this.items[id];
40722         if(!tab){
40723             return null;
40724         }
40725         if(tab == this.active || tab.disabled){
40726             return tab;
40727         }
40728         var e = {};
40729         this.fireEvent("beforetabchange", this, e, tab);
40730         if(e.cancel !== true && !tab.disabled){
40731             if(this.active){
40732                 this.active.hide();
40733             }
40734             this.active = this.items[id];
40735             this.active.show();
40736             this.fireEvent("tabchange", this, this.active);
40737         }
40738         return tab;
40739     },
40740
40741     /**
40742      * Gets the active {@link Roo.TabPanelItem}.
40743      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40744      */
40745     getActiveTab : function(){
40746         return this.active;
40747     },
40748
40749     /**
40750      * Updates the tab body element to fit the height of the container element
40751      * for overflow scrolling
40752      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40753      */
40754     syncHeight : function(targetHeight){
40755         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40756         var bm = this.bodyEl.getMargins();
40757         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40758         this.bodyEl.setHeight(newHeight);
40759         return newHeight;
40760     },
40761
40762     onResize : function(){
40763         if(this.monitorResize){
40764             this.autoSizeTabs();
40765         }
40766     },
40767
40768     /**
40769      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40770      */
40771     beginUpdate : function(){
40772         this.updating = true;
40773     },
40774
40775     /**
40776      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40777      */
40778     endUpdate : function(){
40779         this.updating = false;
40780         this.autoSizeTabs();
40781     },
40782
40783     /**
40784      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40785      */
40786     autoSizeTabs : function()
40787     {
40788         var count = this.items.length;
40789         var vcount = count - this.hiddenCount;
40790         
40791         if (vcount < 2) {
40792             this.stripEl.hide();
40793         } else {
40794             this.stripEl.show();
40795         }
40796         
40797         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40798             return;
40799         }
40800         
40801         
40802         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40803         var availWidth = Math.floor(w / vcount);
40804         var b = this.stripBody;
40805         if(b.getWidth() > w){
40806             var tabs = this.items;
40807             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40808             if(availWidth < this.minTabWidth){
40809                 /*if(!this.sleft){    // incomplete scrolling code
40810                     this.createScrollButtons();
40811                 }
40812                 this.showScroll();
40813                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40814             }
40815         }else{
40816             if(this.currentTabWidth < this.preferredTabWidth){
40817                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40818             }
40819         }
40820     },
40821
40822     /**
40823      * Returns the number of tabs in this TabPanel.
40824      * @return {Number}
40825      */
40826      getCount : function(){
40827          return this.items.length;
40828      },
40829
40830     /**
40831      * Resizes all the tabs to the passed width
40832      * @param {Number} The new width
40833      */
40834     setTabWidth : function(width){
40835         this.currentTabWidth = width;
40836         for(var i = 0, len = this.items.length; i < len; i++) {
40837                 if(!this.items[i].isHidden()) {
40838                 this.items[i].setWidth(width);
40839             }
40840         }
40841     },
40842
40843     /**
40844      * Destroys this TabPanel
40845      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40846      */
40847     destroy : function(removeEl){
40848         Roo.EventManager.removeResizeListener(this.onResize, this);
40849         for(var i = 0, len = this.items.length; i < len; i++){
40850             this.items[i].purgeListeners();
40851         }
40852         if(removeEl === true){
40853             this.el.update("");
40854             this.el.remove();
40855         }
40856     },
40857     
40858     createStrip : function(container)
40859     {
40860         var strip = document.createElement("nav");
40861         strip.className = Roo.bootstrap.version == 4 ?
40862             "navbar-light bg-light" : 
40863             "navbar navbar-default"; //"x-tabs-wrap";
40864         container.appendChild(strip);
40865         return strip;
40866     },
40867     
40868     createStripList : function(strip)
40869     {
40870         // div wrapper for retard IE
40871         // returns the "tr" element.
40872         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40873         //'<div class="x-tabs-strip-wrap">'+
40874           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40875           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40876         return strip.firstChild; //.firstChild.firstChild.firstChild;
40877     },
40878     createBody : function(container)
40879     {
40880         var body = document.createElement("div");
40881         Roo.id(body, "tab-body");
40882         //Roo.fly(body).addClass("x-tabs-body");
40883         Roo.fly(body).addClass("tab-content");
40884         container.appendChild(body);
40885         return body;
40886     },
40887     createItemBody :function(bodyEl, id){
40888         var body = Roo.getDom(id);
40889         if(!body){
40890             body = document.createElement("div");
40891             body.id = id;
40892         }
40893         //Roo.fly(body).addClass("x-tabs-item-body");
40894         Roo.fly(body).addClass("tab-pane");
40895          bodyEl.insertBefore(body, bodyEl.firstChild);
40896         return body;
40897     },
40898     /** @private */
40899     createStripElements :  function(stripEl, text, closable, tpl)
40900     {
40901         var td = document.createElement("li"); // was td..
40902         td.className = 'nav-item';
40903         
40904         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40905         
40906         
40907         stripEl.appendChild(td);
40908         /*if(closable){
40909             td.className = "x-tabs-closable";
40910             if(!this.closeTpl){
40911                 this.closeTpl = new Roo.Template(
40912                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40913                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40914                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40915                 );
40916             }
40917             var el = this.closeTpl.overwrite(td, {"text": text});
40918             var close = el.getElementsByTagName("div")[0];
40919             var inner = el.getElementsByTagName("em")[0];
40920             return {"el": el, "close": close, "inner": inner};
40921         } else {
40922         */
40923         // not sure what this is..
40924 //            if(!this.tabTpl){
40925                 //this.tabTpl = new Roo.Template(
40926                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40927                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40928                 //);
40929 //                this.tabTpl = new Roo.Template(
40930 //                   '<a href="#">' +
40931 //                   '<span unselectable="on"' +
40932 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40933 //                            ' >{text}</span></a>'
40934 //                );
40935 //                
40936 //            }
40937
40938
40939             var template = tpl || this.tabTpl || false;
40940             
40941             if(!template){
40942                 template =  new Roo.Template(
40943                         Roo.bootstrap.version == 4 ? 
40944                             (
40945                                 '<a class="nav-link" href="#" unselectable="on"' +
40946                                      (this.disableTooltips ? '' : ' title="{text}"') +
40947                                      ' >{text}</a>'
40948                             ) : (
40949                                 '<a class="nav-link" href="#">' +
40950                                 '<span unselectable="on"' +
40951                                          (this.disableTooltips ? '' : ' title="{text}"') +
40952                                     ' >{text}</span></a>'
40953                             )
40954                 );
40955             }
40956             
40957             switch (typeof(template)) {
40958                 case 'object' :
40959                     break;
40960                 case 'string' :
40961                     template = new Roo.Template(template);
40962                     break;
40963                 default :
40964                     break;
40965             }
40966             
40967             var el = template.overwrite(td, {"text": text});
40968             
40969             var inner = el.getElementsByTagName("span")[0];
40970             
40971             return {"el": el, "inner": inner};
40972             
40973     }
40974         
40975     
40976 });
40977
40978 /**
40979  * @class Roo.TabPanelItem
40980  * @extends Roo.util.Observable
40981  * Represents an individual item (tab plus body) in a TabPanel.
40982  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40983  * @param {String} id The id of this TabPanelItem
40984  * @param {String} text The text for the tab of this TabPanelItem
40985  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40986  */
40987 Roo.bootstrap.panel.TabItem = function(config){
40988     /**
40989      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40990      * @type Roo.TabPanel
40991      */
40992     this.tabPanel = config.panel;
40993     /**
40994      * The id for this TabPanelItem
40995      * @type String
40996      */
40997     this.id = config.id;
40998     /** @private */
40999     this.disabled = false;
41000     /** @private */
41001     this.text = config.text;
41002     /** @private */
41003     this.loaded = false;
41004     this.closable = config.closable;
41005
41006     /**
41007      * The body element for this TabPanelItem.
41008      * @type Roo.Element
41009      */
41010     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41011     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41012     this.bodyEl.setStyle("display", "block");
41013     this.bodyEl.setStyle("zoom", "1");
41014     //this.hideAction();
41015
41016     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41017     /** @private */
41018     this.el = Roo.get(els.el);
41019     this.inner = Roo.get(els.inner, true);
41020      this.textEl = Roo.bootstrap.version == 4 ?
41021         this.el : Roo.get(this.el.dom.firstChild, true);
41022
41023     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41024     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41025
41026     
41027 //    this.el.on("mousedown", this.onTabMouseDown, this);
41028     this.el.on("click", this.onTabClick, this);
41029     /** @private */
41030     if(config.closable){
41031         var c = Roo.get(els.close, true);
41032         c.dom.title = this.closeText;
41033         c.addClassOnOver("close-over");
41034         c.on("click", this.closeClick, this);
41035      }
41036
41037     this.addEvents({
41038          /**
41039          * @event activate
41040          * Fires when this tab becomes the active tab.
41041          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41042          * @param {Roo.TabPanelItem} this
41043          */
41044         "activate": true,
41045         /**
41046          * @event beforeclose
41047          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41048          * @param {Roo.TabPanelItem} this
41049          * @param {Object} e Set cancel to true on this object to cancel the close.
41050          */
41051         "beforeclose": true,
41052         /**
41053          * @event close
41054          * Fires when this tab is closed.
41055          * @param {Roo.TabPanelItem} this
41056          */
41057          "close": true,
41058         /**
41059          * @event deactivate
41060          * Fires when this tab is no longer the active tab.
41061          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41062          * @param {Roo.TabPanelItem} this
41063          */
41064          "deactivate" : true
41065     });
41066     this.hidden = false;
41067
41068     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41069 };
41070
41071 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41072            {
41073     purgeListeners : function(){
41074        Roo.util.Observable.prototype.purgeListeners.call(this);
41075        this.el.removeAllListeners();
41076     },
41077     /**
41078      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41079      */
41080     show : function(){
41081         this.status_node.addClass("active");
41082         this.showAction();
41083         if(Roo.isOpera){
41084             this.tabPanel.stripWrap.repaint();
41085         }
41086         this.fireEvent("activate", this.tabPanel, this);
41087     },
41088
41089     /**
41090      * Returns true if this tab is the active tab.
41091      * @return {Boolean}
41092      */
41093     isActive : function(){
41094         return this.tabPanel.getActiveTab() == this;
41095     },
41096
41097     /**
41098      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41099      */
41100     hide : function(){
41101         this.status_node.removeClass("active");
41102         this.hideAction();
41103         this.fireEvent("deactivate", this.tabPanel, this);
41104     },
41105
41106     hideAction : function(){
41107         this.bodyEl.hide();
41108         this.bodyEl.setStyle("position", "absolute");
41109         this.bodyEl.setLeft("-20000px");
41110         this.bodyEl.setTop("-20000px");
41111     },
41112
41113     showAction : function(){
41114         this.bodyEl.setStyle("position", "relative");
41115         this.bodyEl.setTop("");
41116         this.bodyEl.setLeft("");
41117         this.bodyEl.show();
41118     },
41119
41120     /**
41121      * Set the tooltip for the tab.
41122      * @param {String} tooltip The tab's tooltip
41123      */
41124     setTooltip : function(text){
41125         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41126             this.textEl.dom.qtip = text;
41127             this.textEl.dom.removeAttribute('title');
41128         }else{
41129             this.textEl.dom.title = text;
41130         }
41131     },
41132
41133     onTabClick : function(e){
41134         e.preventDefault();
41135         this.tabPanel.activate(this.id);
41136     },
41137
41138     onTabMouseDown : function(e){
41139         e.preventDefault();
41140         this.tabPanel.activate(this.id);
41141     },
41142 /*
41143     getWidth : function(){
41144         return this.inner.getWidth();
41145     },
41146
41147     setWidth : function(width){
41148         var iwidth = width - this.linode.getPadding("lr");
41149         this.inner.setWidth(iwidth);
41150         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41151         this.linode.setWidth(width);
41152     },
41153 */
41154     /**
41155      * Show or hide the tab
41156      * @param {Boolean} hidden True to hide or false to show.
41157      */
41158     setHidden : function(hidden){
41159         this.hidden = hidden;
41160         this.linode.setStyle("display", hidden ? "none" : "");
41161     },
41162
41163     /**
41164      * Returns true if this tab is "hidden"
41165      * @return {Boolean}
41166      */
41167     isHidden : function(){
41168         return this.hidden;
41169     },
41170
41171     /**
41172      * Returns the text for this tab
41173      * @return {String}
41174      */
41175     getText : function(){
41176         return this.text;
41177     },
41178     /*
41179     autoSize : function(){
41180         //this.el.beginMeasure();
41181         this.textEl.setWidth(1);
41182         /*
41183          *  #2804 [new] Tabs in Roojs
41184          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41185          */
41186         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41187         //this.el.endMeasure();
41188     //},
41189
41190     /**
41191      * Sets the text for the tab (Note: this also sets the tooltip text)
41192      * @param {String} text The tab's text and tooltip
41193      */
41194     setText : function(text){
41195         this.text = text;
41196         this.textEl.update(text);
41197         this.setTooltip(text);
41198         //if(!this.tabPanel.resizeTabs){
41199         //    this.autoSize();
41200         //}
41201     },
41202     /**
41203      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41204      */
41205     activate : function(){
41206         this.tabPanel.activate(this.id);
41207     },
41208
41209     /**
41210      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41211      */
41212     disable : function(){
41213         if(this.tabPanel.active != this){
41214             this.disabled = true;
41215             this.status_node.addClass("disabled");
41216         }
41217     },
41218
41219     /**
41220      * Enables this TabPanelItem if it was previously disabled.
41221      */
41222     enable : function(){
41223         this.disabled = false;
41224         this.status_node.removeClass("disabled");
41225     },
41226
41227     /**
41228      * Sets the content for this TabPanelItem.
41229      * @param {String} content The content
41230      * @param {Boolean} loadScripts true to look for and load scripts
41231      */
41232     setContent : function(content, loadScripts){
41233         this.bodyEl.update(content, loadScripts);
41234     },
41235
41236     /**
41237      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41238      * @return {Roo.UpdateManager} The UpdateManager
41239      */
41240     getUpdateManager : function(){
41241         return this.bodyEl.getUpdateManager();
41242     },
41243
41244     /**
41245      * Set a URL to be used to load the content for this TabPanelItem.
41246      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41247      * @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)
41248      * @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)
41249      * @return {Roo.UpdateManager} The UpdateManager
41250      */
41251     setUrl : function(url, params, loadOnce){
41252         if(this.refreshDelegate){
41253             this.un('activate', this.refreshDelegate);
41254         }
41255         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41256         this.on("activate", this.refreshDelegate);
41257         return this.bodyEl.getUpdateManager();
41258     },
41259
41260     /** @private */
41261     _handleRefresh : function(url, params, loadOnce){
41262         if(!loadOnce || !this.loaded){
41263             var updater = this.bodyEl.getUpdateManager();
41264             updater.update(url, params, this._setLoaded.createDelegate(this));
41265         }
41266     },
41267
41268     /**
41269      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41270      *   Will fail silently if the setUrl method has not been called.
41271      *   This does not activate the panel, just updates its content.
41272      */
41273     refresh : function(){
41274         if(this.refreshDelegate){
41275            this.loaded = false;
41276            this.refreshDelegate();
41277         }
41278     },
41279
41280     /** @private */
41281     _setLoaded : function(){
41282         this.loaded = true;
41283     },
41284
41285     /** @private */
41286     closeClick : function(e){
41287         var o = {};
41288         e.stopEvent();
41289         this.fireEvent("beforeclose", this, o);
41290         if(o.cancel !== true){
41291             this.tabPanel.removeTab(this.id);
41292         }
41293     },
41294     /**
41295      * The text displayed in the tooltip for the close icon.
41296      * @type String
41297      */
41298     closeText : "Close this tab"
41299 });
41300 /**
41301 *    This script refer to:
41302 *    Title: International Telephone Input
41303 *    Author: Jack O'Connor
41304 *    Code version:  v12.1.12
41305 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41306 **/
41307
41308 Roo.bootstrap.PhoneInputData = function() {
41309     var d = [
41310       [
41311         "Afghanistan (‫افغانستان‬‎)",
41312         "af",
41313         "93"
41314       ],
41315       [
41316         "Albania (Shqipëri)",
41317         "al",
41318         "355"
41319       ],
41320       [
41321         "Algeria (‫الجزائر‬‎)",
41322         "dz",
41323         "213"
41324       ],
41325       [
41326         "American Samoa",
41327         "as",
41328         "1684"
41329       ],
41330       [
41331         "Andorra",
41332         "ad",
41333         "376"
41334       ],
41335       [
41336         "Angola",
41337         "ao",
41338         "244"
41339       ],
41340       [
41341         "Anguilla",
41342         "ai",
41343         "1264"
41344       ],
41345       [
41346         "Antigua and Barbuda",
41347         "ag",
41348         "1268"
41349       ],
41350       [
41351         "Argentina",
41352         "ar",
41353         "54"
41354       ],
41355       [
41356         "Armenia (Հայաստան)",
41357         "am",
41358         "374"
41359       ],
41360       [
41361         "Aruba",
41362         "aw",
41363         "297"
41364       ],
41365       [
41366         "Australia",
41367         "au",
41368         "61",
41369         0
41370       ],
41371       [
41372         "Austria (Österreich)",
41373         "at",
41374         "43"
41375       ],
41376       [
41377         "Azerbaijan (Azərbaycan)",
41378         "az",
41379         "994"
41380       ],
41381       [
41382         "Bahamas",
41383         "bs",
41384         "1242"
41385       ],
41386       [
41387         "Bahrain (‫البحرين‬‎)",
41388         "bh",
41389         "973"
41390       ],
41391       [
41392         "Bangladesh (বাংলাদেশ)",
41393         "bd",
41394         "880"
41395       ],
41396       [
41397         "Barbados",
41398         "bb",
41399         "1246"
41400       ],
41401       [
41402         "Belarus (Беларусь)",
41403         "by",
41404         "375"
41405       ],
41406       [
41407         "Belgium (België)",
41408         "be",
41409         "32"
41410       ],
41411       [
41412         "Belize",
41413         "bz",
41414         "501"
41415       ],
41416       [
41417         "Benin (Bénin)",
41418         "bj",
41419         "229"
41420       ],
41421       [
41422         "Bermuda",
41423         "bm",
41424         "1441"
41425       ],
41426       [
41427         "Bhutan (འབྲུག)",
41428         "bt",
41429         "975"
41430       ],
41431       [
41432         "Bolivia",
41433         "bo",
41434         "591"
41435       ],
41436       [
41437         "Bosnia and Herzegovina (Босна и Херцеговина)",
41438         "ba",
41439         "387"
41440       ],
41441       [
41442         "Botswana",
41443         "bw",
41444         "267"
41445       ],
41446       [
41447         "Brazil (Brasil)",
41448         "br",
41449         "55"
41450       ],
41451       [
41452         "British Indian Ocean Territory",
41453         "io",
41454         "246"
41455       ],
41456       [
41457         "British Virgin Islands",
41458         "vg",
41459         "1284"
41460       ],
41461       [
41462         "Brunei",
41463         "bn",
41464         "673"
41465       ],
41466       [
41467         "Bulgaria (България)",
41468         "bg",
41469         "359"
41470       ],
41471       [
41472         "Burkina Faso",
41473         "bf",
41474         "226"
41475       ],
41476       [
41477         "Burundi (Uburundi)",
41478         "bi",
41479         "257"
41480       ],
41481       [
41482         "Cambodia (កម្ពុជា)",
41483         "kh",
41484         "855"
41485       ],
41486       [
41487         "Cameroon (Cameroun)",
41488         "cm",
41489         "237"
41490       ],
41491       [
41492         "Canada",
41493         "ca",
41494         "1",
41495         1,
41496         ["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"]
41497       ],
41498       [
41499         "Cape Verde (Kabu Verdi)",
41500         "cv",
41501         "238"
41502       ],
41503       [
41504         "Caribbean Netherlands",
41505         "bq",
41506         "599",
41507         1
41508       ],
41509       [
41510         "Cayman Islands",
41511         "ky",
41512         "1345"
41513       ],
41514       [
41515         "Central African Republic (République centrafricaine)",
41516         "cf",
41517         "236"
41518       ],
41519       [
41520         "Chad (Tchad)",
41521         "td",
41522         "235"
41523       ],
41524       [
41525         "Chile",
41526         "cl",
41527         "56"
41528       ],
41529       [
41530         "China (中国)",
41531         "cn",
41532         "86"
41533       ],
41534       [
41535         "Christmas Island",
41536         "cx",
41537         "61",
41538         2
41539       ],
41540       [
41541         "Cocos (Keeling) Islands",
41542         "cc",
41543         "61",
41544         1
41545       ],
41546       [
41547         "Colombia",
41548         "co",
41549         "57"
41550       ],
41551       [
41552         "Comoros (‫جزر القمر‬‎)",
41553         "km",
41554         "269"
41555       ],
41556       [
41557         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41558         "cd",
41559         "243"
41560       ],
41561       [
41562         "Congo (Republic) (Congo-Brazzaville)",
41563         "cg",
41564         "242"
41565       ],
41566       [
41567         "Cook Islands",
41568         "ck",
41569         "682"
41570       ],
41571       [
41572         "Costa Rica",
41573         "cr",
41574         "506"
41575       ],
41576       [
41577         "Côte d’Ivoire",
41578         "ci",
41579         "225"
41580       ],
41581       [
41582         "Croatia (Hrvatska)",
41583         "hr",
41584         "385"
41585       ],
41586       [
41587         "Cuba",
41588         "cu",
41589         "53"
41590       ],
41591       [
41592         "Curaçao",
41593         "cw",
41594         "599",
41595         0
41596       ],
41597       [
41598         "Cyprus (Κύπρος)",
41599         "cy",
41600         "357"
41601       ],
41602       [
41603         "Czech Republic (Česká republika)",
41604         "cz",
41605         "420"
41606       ],
41607       [
41608         "Denmark (Danmark)",
41609         "dk",
41610         "45"
41611       ],
41612       [
41613         "Djibouti",
41614         "dj",
41615         "253"
41616       ],
41617       [
41618         "Dominica",
41619         "dm",
41620         "1767"
41621       ],
41622       [
41623         "Dominican Republic (República Dominicana)",
41624         "do",
41625         "1",
41626         2,
41627         ["809", "829", "849"]
41628       ],
41629       [
41630         "Ecuador",
41631         "ec",
41632         "593"
41633       ],
41634       [
41635         "Egypt (‫مصر‬‎)",
41636         "eg",
41637         "20"
41638       ],
41639       [
41640         "El Salvador",
41641         "sv",
41642         "503"
41643       ],
41644       [
41645         "Equatorial Guinea (Guinea Ecuatorial)",
41646         "gq",
41647         "240"
41648       ],
41649       [
41650         "Eritrea",
41651         "er",
41652         "291"
41653       ],
41654       [
41655         "Estonia (Eesti)",
41656         "ee",
41657         "372"
41658       ],
41659       [
41660         "Ethiopia",
41661         "et",
41662         "251"
41663       ],
41664       [
41665         "Falkland Islands (Islas Malvinas)",
41666         "fk",
41667         "500"
41668       ],
41669       [
41670         "Faroe Islands (Føroyar)",
41671         "fo",
41672         "298"
41673       ],
41674       [
41675         "Fiji",
41676         "fj",
41677         "679"
41678       ],
41679       [
41680         "Finland (Suomi)",
41681         "fi",
41682         "358",
41683         0
41684       ],
41685       [
41686         "France",
41687         "fr",
41688         "33"
41689       ],
41690       [
41691         "French Guiana (Guyane française)",
41692         "gf",
41693         "594"
41694       ],
41695       [
41696         "French Polynesia (Polynésie française)",
41697         "pf",
41698         "689"
41699       ],
41700       [
41701         "Gabon",
41702         "ga",
41703         "241"
41704       ],
41705       [
41706         "Gambia",
41707         "gm",
41708         "220"
41709       ],
41710       [
41711         "Georgia (საქართველო)",
41712         "ge",
41713         "995"
41714       ],
41715       [
41716         "Germany (Deutschland)",
41717         "de",
41718         "49"
41719       ],
41720       [
41721         "Ghana (Gaana)",
41722         "gh",
41723         "233"
41724       ],
41725       [
41726         "Gibraltar",
41727         "gi",
41728         "350"
41729       ],
41730       [
41731         "Greece (Ελλάδα)",
41732         "gr",
41733         "30"
41734       ],
41735       [
41736         "Greenland (Kalaallit Nunaat)",
41737         "gl",
41738         "299"
41739       ],
41740       [
41741         "Grenada",
41742         "gd",
41743         "1473"
41744       ],
41745       [
41746         "Guadeloupe",
41747         "gp",
41748         "590",
41749         0
41750       ],
41751       [
41752         "Guam",
41753         "gu",
41754         "1671"
41755       ],
41756       [
41757         "Guatemala",
41758         "gt",
41759         "502"
41760       ],
41761       [
41762         "Guernsey",
41763         "gg",
41764         "44",
41765         1
41766       ],
41767       [
41768         "Guinea (Guinée)",
41769         "gn",
41770         "224"
41771       ],
41772       [
41773         "Guinea-Bissau (Guiné Bissau)",
41774         "gw",
41775         "245"
41776       ],
41777       [
41778         "Guyana",
41779         "gy",
41780         "592"
41781       ],
41782       [
41783         "Haiti",
41784         "ht",
41785         "509"
41786       ],
41787       [
41788         "Honduras",
41789         "hn",
41790         "504"
41791       ],
41792       [
41793         "Hong Kong (香港)",
41794         "hk",
41795         "852"
41796       ],
41797       [
41798         "Hungary (Magyarország)",
41799         "hu",
41800         "36"
41801       ],
41802       [
41803         "Iceland (Ísland)",
41804         "is",
41805         "354"
41806       ],
41807       [
41808         "India (भारत)",
41809         "in",
41810         "91"
41811       ],
41812       [
41813         "Indonesia",
41814         "id",
41815         "62"
41816       ],
41817       [
41818         "Iran (‫ایران‬‎)",
41819         "ir",
41820         "98"
41821       ],
41822       [
41823         "Iraq (‫العراق‬‎)",
41824         "iq",
41825         "964"
41826       ],
41827       [
41828         "Ireland",
41829         "ie",
41830         "353"
41831       ],
41832       [
41833         "Isle of Man",
41834         "im",
41835         "44",
41836         2
41837       ],
41838       [
41839         "Israel (‫ישראל‬‎)",
41840         "il",
41841         "972"
41842       ],
41843       [
41844         "Italy (Italia)",
41845         "it",
41846         "39",
41847         0
41848       ],
41849       [
41850         "Jamaica",
41851         "jm",
41852         "1876"
41853       ],
41854       [
41855         "Japan (日本)",
41856         "jp",
41857         "81"
41858       ],
41859       [
41860         "Jersey",
41861         "je",
41862         "44",
41863         3
41864       ],
41865       [
41866         "Jordan (‫الأردن‬‎)",
41867         "jo",
41868         "962"
41869       ],
41870       [
41871         "Kazakhstan (Казахстан)",
41872         "kz",
41873         "7",
41874         1
41875       ],
41876       [
41877         "Kenya",
41878         "ke",
41879         "254"
41880       ],
41881       [
41882         "Kiribati",
41883         "ki",
41884         "686"
41885       ],
41886       [
41887         "Kosovo",
41888         "xk",
41889         "383"
41890       ],
41891       [
41892         "Kuwait (‫الكويت‬‎)",
41893         "kw",
41894         "965"
41895       ],
41896       [
41897         "Kyrgyzstan (Кыргызстан)",
41898         "kg",
41899         "996"
41900       ],
41901       [
41902         "Laos (ລາວ)",
41903         "la",
41904         "856"
41905       ],
41906       [
41907         "Latvia (Latvija)",
41908         "lv",
41909         "371"
41910       ],
41911       [
41912         "Lebanon (‫لبنان‬‎)",
41913         "lb",
41914         "961"
41915       ],
41916       [
41917         "Lesotho",
41918         "ls",
41919         "266"
41920       ],
41921       [
41922         "Liberia",
41923         "lr",
41924         "231"
41925       ],
41926       [
41927         "Libya (‫ليبيا‬‎)",
41928         "ly",
41929         "218"
41930       ],
41931       [
41932         "Liechtenstein",
41933         "li",
41934         "423"
41935       ],
41936       [
41937         "Lithuania (Lietuva)",
41938         "lt",
41939         "370"
41940       ],
41941       [
41942         "Luxembourg",
41943         "lu",
41944         "352"
41945       ],
41946       [
41947         "Macau (澳門)",
41948         "mo",
41949         "853"
41950       ],
41951       [
41952         "Macedonia (FYROM) (Македонија)",
41953         "mk",
41954         "389"
41955       ],
41956       [
41957         "Madagascar (Madagasikara)",
41958         "mg",
41959         "261"
41960       ],
41961       [
41962         "Malawi",
41963         "mw",
41964         "265"
41965       ],
41966       [
41967         "Malaysia",
41968         "my",
41969         "60"
41970       ],
41971       [
41972         "Maldives",
41973         "mv",
41974         "960"
41975       ],
41976       [
41977         "Mali",
41978         "ml",
41979         "223"
41980       ],
41981       [
41982         "Malta",
41983         "mt",
41984         "356"
41985       ],
41986       [
41987         "Marshall Islands",
41988         "mh",
41989         "692"
41990       ],
41991       [
41992         "Martinique",
41993         "mq",
41994         "596"
41995       ],
41996       [
41997         "Mauritania (‫موريتانيا‬‎)",
41998         "mr",
41999         "222"
42000       ],
42001       [
42002         "Mauritius (Moris)",
42003         "mu",
42004         "230"
42005       ],
42006       [
42007         "Mayotte",
42008         "yt",
42009         "262",
42010         1
42011       ],
42012       [
42013         "Mexico (México)",
42014         "mx",
42015         "52"
42016       ],
42017       [
42018         "Micronesia",
42019         "fm",
42020         "691"
42021       ],
42022       [
42023         "Moldova (Republica Moldova)",
42024         "md",
42025         "373"
42026       ],
42027       [
42028         "Monaco",
42029         "mc",
42030         "377"
42031       ],
42032       [
42033         "Mongolia (Монгол)",
42034         "mn",
42035         "976"
42036       ],
42037       [
42038         "Montenegro (Crna Gora)",
42039         "me",
42040         "382"
42041       ],
42042       [
42043         "Montserrat",
42044         "ms",
42045         "1664"
42046       ],
42047       [
42048         "Morocco (‫المغرب‬‎)",
42049         "ma",
42050         "212",
42051         0
42052       ],
42053       [
42054         "Mozambique (Moçambique)",
42055         "mz",
42056         "258"
42057       ],
42058       [
42059         "Myanmar (Burma) (မြန်မာ)",
42060         "mm",
42061         "95"
42062       ],
42063       [
42064         "Namibia (Namibië)",
42065         "na",
42066         "264"
42067       ],
42068       [
42069         "Nauru",
42070         "nr",
42071         "674"
42072       ],
42073       [
42074         "Nepal (नेपाल)",
42075         "np",
42076         "977"
42077       ],
42078       [
42079         "Netherlands (Nederland)",
42080         "nl",
42081         "31"
42082       ],
42083       [
42084         "New Caledonia (Nouvelle-Calédonie)",
42085         "nc",
42086         "687"
42087       ],
42088       [
42089         "New Zealand",
42090         "nz",
42091         "64"
42092       ],
42093       [
42094         "Nicaragua",
42095         "ni",
42096         "505"
42097       ],
42098       [
42099         "Niger (Nijar)",
42100         "ne",
42101         "227"
42102       ],
42103       [
42104         "Nigeria",
42105         "ng",
42106         "234"
42107       ],
42108       [
42109         "Niue",
42110         "nu",
42111         "683"
42112       ],
42113       [
42114         "Norfolk Island",
42115         "nf",
42116         "672"
42117       ],
42118       [
42119         "North Korea (조선 민주주의 인민 공화국)",
42120         "kp",
42121         "850"
42122       ],
42123       [
42124         "Northern Mariana Islands",
42125         "mp",
42126         "1670"
42127       ],
42128       [
42129         "Norway (Norge)",
42130         "no",
42131         "47",
42132         0
42133       ],
42134       [
42135         "Oman (‫عُمان‬‎)",
42136         "om",
42137         "968"
42138       ],
42139       [
42140         "Pakistan (‫پاکستان‬‎)",
42141         "pk",
42142         "92"
42143       ],
42144       [
42145         "Palau",
42146         "pw",
42147         "680"
42148       ],
42149       [
42150         "Palestine (‫فلسطين‬‎)",
42151         "ps",
42152         "970"
42153       ],
42154       [
42155         "Panama (Panamá)",
42156         "pa",
42157         "507"
42158       ],
42159       [
42160         "Papua New Guinea",
42161         "pg",
42162         "675"
42163       ],
42164       [
42165         "Paraguay",
42166         "py",
42167         "595"
42168       ],
42169       [
42170         "Peru (Perú)",
42171         "pe",
42172         "51"
42173       ],
42174       [
42175         "Philippines",
42176         "ph",
42177         "63"
42178       ],
42179       [
42180         "Poland (Polska)",
42181         "pl",
42182         "48"
42183       ],
42184       [
42185         "Portugal",
42186         "pt",
42187         "351"
42188       ],
42189       [
42190         "Puerto Rico",
42191         "pr",
42192         "1",
42193         3,
42194         ["787", "939"]
42195       ],
42196       [
42197         "Qatar (‫قطر‬‎)",
42198         "qa",
42199         "974"
42200       ],
42201       [
42202         "Réunion (La Réunion)",
42203         "re",
42204         "262",
42205         0
42206       ],
42207       [
42208         "Romania (România)",
42209         "ro",
42210         "40"
42211       ],
42212       [
42213         "Russia (Россия)",
42214         "ru",
42215         "7",
42216         0
42217       ],
42218       [
42219         "Rwanda",
42220         "rw",
42221         "250"
42222       ],
42223       [
42224         "Saint Barthélemy",
42225         "bl",
42226         "590",
42227         1
42228       ],
42229       [
42230         "Saint Helena",
42231         "sh",
42232         "290"
42233       ],
42234       [
42235         "Saint Kitts and Nevis",
42236         "kn",
42237         "1869"
42238       ],
42239       [
42240         "Saint Lucia",
42241         "lc",
42242         "1758"
42243       ],
42244       [
42245         "Saint Martin (Saint-Martin (partie française))",
42246         "mf",
42247         "590",
42248         2
42249       ],
42250       [
42251         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42252         "pm",
42253         "508"
42254       ],
42255       [
42256         "Saint Vincent and the Grenadines",
42257         "vc",
42258         "1784"
42259       ],
42260       [
42261         "Samoa",
42262         "ws",
42263         "685"
42264       ],
42265       [
42266         "San Marino",
42267         "sm",
42268         "378"
42269       ],
42270       [
42271         "São Tomé and Príncipe (São Tomé e Príncipe)",
42272         "st",
42273         "239"
42274       ],
42275       [
42276         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42277         "sa",
42278         "966"
42279       ],
42280       [
42281         "Senegal (Sénégal)",
42282         "sn",
42283         "221"
42284       ],
42285       [
42286         "Serbia (Србија)",
42287         "rs",
42288         "381"
42289       ],
42290       [
42291         "Seychelles",
42292         "sc",
42293         "248"
42294       ],
42295       [
42296         "Sierra Leone",
42297         "sl",
42298         "232"
42299       ],
42300       [
42301         "Singapore",
42302         "sg",
42303         "65"
42304       ],
42305       [
42306         "Sint Maarten",
42307         "sx",
42308         "1721"
42309       ],
42310       [
42311         "Slovakia (Slovensko)",
42312         "sk",
42313         "421"
42314       ],
42315       [
42316         "Slovenia (Slovenija)",
42317         "si",
42318         "386"
42319       ],
42320       [
42321         "Solomon Islands",
42322         "sb",
42323         "677"
42324       ],
42325       [
42326         "Somalia (Soomaaliya)",
42327         "so",
42328         "252"
42329       ],
42330       [
42331         "South Africa",
42332         "za",
42333         "27"
42334       ],
42335       [
42336         "South Korea (대한민국)",
42337         "kr",
42338         "82"
42339       ],
42340       [
42341         "South Sudan (‫جنوب السودان‬‎)",
42342         "ss",
42343         "211"
42344       ],
42345       [
42346         "Spain (España)",
42347         "es",
42348         "34"
42349       ],
42350       [
42351         "Sri Lanka (ශ්‍රී ලංකාව)",
42352         "lk",
42353         "94"
42354       ],
42355       [
42356         "Sudan (‫السودان‬‎)",
42357         "sd",
42358         "249"
42359       ],
42360       [
42361         "Suriname",
42362         "sr",
42363         "597"
42364       ],
42365       [
42366         "Svalbard and Jan Mayen",
42367         "sj",
42368         "47",
42369         1
42370       ],
42371       [
42372         "Swaziland",
42373         "sz",
42374         "268"
42375       ],
42376       [
42377         "Sweden (Sverige)",
42378         "se",
42379         "46"
42380       ],
42381       [
42382         "Switzerland (Schweiz)",
42383         "ch",
42384         "41"
42385       ],
42386       [
42387         "Syria (‫سوريا‬‎)",
42388         "sy",
42389         "963"
42390       ],
42391       [
42392         "Taiwan (台灣)",
42393         "tw",
42394         "886"
42395       ],
42396       [
42397         "Tajikistan",
42398         "tj",
42399         "992"
42400       ],
42401       [
42402         "Tanzania",
42403         "tz",
42404         "255"
42405       ],
42406       [
42407         "Thailand (ไทย)",
42408         "th",
42409         "66"
42410       ],
42411       [
42412         "Timor-Leste",
42413         "tl",
42414         "670"
42415       ],
42416       [
42417         "Togo",
42418         "tg",
42419         "228"
42420       ],
42421       [
42422         "Tokelau",
42423         "tk",
42424         "690"
42425       ],
42426       [
42427         "Tonga",
42428         "to",
42429         "676"
42430       ],
42431       [
42432         "Trinidad and Tobago",
42433         "tt",
42434         "1868"
42435       ],
42436       [
42437         "Tunisia (‫تونس‬‎)",
42438         "tn",
42439         "216"
42440       ],
42441       [
42442         "Turkey (Türkiye)",
42443         "tr",
42444         "90"
42445       ],
42446       [
42447         "Turkmenistan",
42448         "tm",
42449         "993"
42450       ],
42451       [
42452         "Turks and Caicos Islands",
42453         "tc",
42454         "1649"
42455       ],
42456       [
42457         "Tuvalu",
42458         "tv",
42459         "688"
42460       ],
42461       [
42462         "U.S. Virgin Islands",
42463         "vi",
42464         "1340"
42465       ],
42466       [
42467         "Uganda",
42468         "ug",
42469         "256"
42470       ],
42471       [
42472         "Ukraine (Україна)",
42473         "ua",
42474         "380"
42475       ],
42476       [
42477         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42478         "ae",
42479         "971"
42480       ],
42481       [
42482         "United Kingdom",
42483         "gb",
42484         "44",
42485         0
42486       ],
42487       [
42488         "United States",
42489         "us",
42490         "1",
42491         0
42492       ],
42493       [
42494         "Uruguay",
42495         "uy",
42496         "598"
42497       ],
42498       [
42499         "Uzbekistan (Oʻzbekiston)",
42500         "uz",
42501         "998"
42502       ],
42503       [
42504         "Vanuatu",
42505         "vu",
42506         "678"
42507       ],
42508       [
42509         "Vatican City (Città del Vaticano)",
42510         "va",
42511         "39",
42512         1
42513       ],
42514       [
42515         "Venezuela",
42516         "ve",
42517         "58"
42518       ],
42519       [
42520         "Vietnam (Việt Nam)",
42521         "vn",
42522         "84"
42523       ],
42524       [
42525         "Wallis and Futuna (Wallis-et-Futuna)",
42526         "wf",
42527         "681"
42528       ],
42529       [
42530         "Western Sahara (‫الصحراء الغربية‬‎)",
42531         "eh",
42532         "212",
42533         1
42534       ],
42535       [
42536         "Yemen (‫اليمن‬‎)",
42537         "ye",
42538         "967"
42539       ],
42540       [
42541         "Zambia",
42542         "zm",
42543         "260"
42544       ],
42545       [
42546         "Zimbabwe",
42547         "zw",
42548         "263"
42549       ],
42550       [
42551         "Åland Islands",
42552         "ax",
42553         "358",
42554         1
42555       ]
42556   ];
42557   
42558   return d;
42559 }/**
42560 *    This script refer to:
42561 *    Title: International Telephone Input
42562 *    Author: Jack O'Connor
42563 *    Code version:  v12.1.12
42564 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42565 **/
42566
42567 /**
42568  * @class Roo.bootstrap.PhoneInput
42569  * @extends Roo.bootstrap.TriggerField
42570  * An input with International dial-code selection
42571  
42572  * @cfg {String} defaultDialCode default '+852'
42573  * @cfg {Array} preferedCountries default []
42574   
42575  * @constructor
42576  * Create a new PhoneInput.
42577  * @param {Object} config Configuration options
42578  */
42579
42580 Roo.bootstrap.PhoneInput = function(config) {
42581     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42582 };
42583
42584 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42585         
42586         listWidth: undefined,
42587         
42588         selectedClass: 'active',
42589         
42590         invalidClass : "has-warning",
42591         
42592         validClass: 'has-success',
42593         
42594         allowed: '0123456789',
42595         
42596         max_length: 15,
42597         
42598         /**
42599          * @cfg {String} defaultDialCode The default dial code when initializing the input
42600          */
42601         defaultDialCode: '+852',
42602         
42603         /**
42604          * @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
42605          */
42606         preferedCountries: false,
42607         
42608         getAutoCreate : function()
42609         {
42610             var data = Roo.bootstrap.PhoneInputData();
42611             var align = this.labelAlign || this.parentLabelAlign();
42612             var id = Roo.id();
42613             
42614             this.allCountries = [];
42615             this.dialCodeMapping = [];
42616             
42617             for (var i = 0; i < data.length; i++) {
42618               var c = data[i];
42619               this.allCountries[i] = {
42620                 name: c[0],
42621                 iso2: c[1],
42622                 dialCode: c[2],
42623                 priority: c[3] || 0,
42624                 areaCodes: c[4] || null
42625               };
42626               this.dialCodeMapping[c[2]] = {
42627                   name: c[0],
42628                   iso2: c[1],
42629                   priority: c[3] || 0,
42630                   areaCodes: c[4] || null
42631               };
42632             }
42633             
42634             var cfg = {
42635                 cls: 'form-group',
42636                 cn: []
42637             };
42638             
42639             var input =  {
42640                 tag: 'input',
42641                 id : id,
42642                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42643                 maxlength: this.max_length,
42644                 cls : 'form-control tel-input',
42645                 autocomplete: 'new-password'
42646             };
42647             
42648             var hiddenInput = {
42649                 tag: 'input',
42650                 type: 'hidden',
42651                 cls: 'hidden-tel-input'
42652             };
42653             
42654             if (this.name) {
42655                 hiddenInput.name = this.name;
42656             }
42657             
42658             if (this.disabled) {
42659                 input.disabled = true;
42660             }
42661             
42662             var flag_container = {
42663                 tag: 'div',
42664                 cls: 'flag-box',
42665                 cn: [
42666                     {
42667                         tag: 'div',
42668                         cls: 'flag'
42669                     },
42670                     {
42671                         tag: 'div',
42672                         cls: 'caret'
42673                     }
42674                 ]
42675             };
42676             
42677             var box = {
42678                 tag: 'div',
42679                 cls: this.hasFeedback ? 'has-feedback' : '',
42680                 cn: [
42681                     hiddenInput,
42682                     input,
42683                     {
42684                         tag: 'input',
42685                         cls: 'dial-code-holder',
42686                         disabled: true
42687                     }
42688                 ]
42689             };
42690             
42691             var container = {
42692                 cls: 'roo-select2-container input-group',
42693                 cn: [
42694                     flag_container,
42695                     box
42696                 ]
42697             };
42698             
42699             if (this.fieldLabel.length) {
42700                 var indicator = {
42701                     tag: 'i',
42702                     tooltip: 'This field is required'
42703                 };
42704                 
42705                 var label = {
42706                     tag: 'label',
42707                     'for':  id,
42708                     cls: 'control-label',
42709                     cn: []
42710                 };
42711                 
42712                 var label_text = {
42713                     tag: 'span',
42714                     html: this.fieldLabel
42715                 };
42716                 
42717                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42718                 label.cn = [
42719                     indicator,
42720                     label_text
42721                 ];
42722                 
42723                 if(this.indicatorpos == 'right') {
42724                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42725                     label.cn = [
42726                         label_text,
42727                         indicator
42728                     ];
42729                 }
42730                 
42731                 if(align == 'left') {
42732                     container = {
42733                         tag: 'div',
42734                         cn: [
42735                             container
42736                         ]
42737                     };
42738                     
42739                     if(this.labelWidth > 12){
42740                         label.style = "width: " + this.labelWidth + 'px';
42741                     }
42742                     if(this.labelWidth < 13 && this.labelmd == 0){
42743                         this.labelmd = this.labelWidth;
42744                     }
42745                     if(this.labellg > 0){
42746                         label.cls += ' col-lg-' + this.labellg;
42747                         input.cls += ' col-lg-' + (12 - this.labellg);
42748                     }
42749                     if(this.labelmd > 0){
42750                         label.cls += ' col-md-' + this.labelmd;
42751                         container.cls += ' col-md-' + (12 - this.labelmd);
42752                     }
42753                     if(this.labelsm > 0){
42754                         label.cls += ' col-sm-' + this.labelsm;
42755                         container.cls += ' col-sm-' + (12 - this.labelsm);
42756                     }
42757                     if(this.labelxs > 0){
42758                         label.cls += ' col-xs-' + this.labelxs;
42759                         container.cls += ' col-xs-' + (12 - this.labelxs);
42760                     }
42761                 }
42762             }
42763             
42764             cfg.cn = [
42765                 label,
42766                 container
42767             ];
42768             
42769             var settings = this;
42770             
42771             ['xs','sm','md','lg'].map(function(size){
42772                 if (settings[size]) {
42773                     cfg.cls += ' col-' + size + '-' + settings[size];
42774                 }
42775             });
42776             
42777             this.store = new Roo.data.Store({
42778                 proxy : new Roo.data.MemoryProxy({}),
42779                 reader : new Roo.data.JsonReader({
42780                     fields : [
42781                         {
42782                             'name' : 'name',
42783                             'type' : 'string'
42784                         },
42785                         {
42786                             'name' : 'iso2',
42787                             'type' : 'string'
42788                         },
42789                         {
42790                             'name' : 'dialCode',
42791                             'type' : 'string'
42792                         },
42793                         {
42794                             'name' : 'priority',
42795                             'type' : 'string'
42796                         },
42797                         {
42798                             'name' : 'areaCodes',
42799                             'type' : 'string'
42800                         }
42801                     ]
42802                 })
42803             });
42804             
42805             if(!this.preferedCountries) {
42806                 this.preferedCountries = [
42807                     'hk',
42808                     'gb',
42809                     'us'
42810                 ];
42811             }
42812             
42813             var p = this.preferedCountries.reverse();
42814             
42815             if(p) {
42816                 for (var i = 0; i < p.length; i++) {
42817                     for (var j = 0; j < this.allCountries.length; j++) {
42818                         if(this.allCountries[j].iso2 == p[i]) {
42819                             var t = this.allCountries[j];
42820                             this.allCountries.splice(j,1);
42821                             this.allCountries.unshift(t);
42822                         }
42823                     } 
42824                 }
42825             }
42826             
42827             this.store.proxy.data = {
42828                 success: true,
42829                 data: this.allCountries
42830             };
42831             
42832             return cfg;
42833         },
42834         
42835         initEvents : function()
42836         {
42837             this.createList();
42838             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42839             
42840             this.indicator = this.indicatorEl();
42841             this.flag = this.flagEl();
42842             this.dialCodeHolder = this.dialCodeHolderEl();
42843             
42844             this.trigger = this.el.select('div.flag-box',true).first();
42845             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42846             
42847             var _this = this;
42848             
42849             (function(){
42850                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42851                 _this.list.setWidth(lw);
42852             }).defer(100);
42853             
42854             this.list.on('mouseover', this.onViewOver, this);
42855             this.list.on('mousemove', this.onViewMove, this);
42856             this.inputEl().on("keyup", this.onKeyUp, this);
42857             this.inputEl().on("keypress", this.onKeyPress, this);
42858             
42859             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42860
42861             this.view = new Roo.View(this.list, this.tpl, {
42862                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42863             });
42864             
42865             this.view.on('click', this.onViewClick, this);
42866             this.setValue(this.defaultDialCode);
42867         },
42868         
42869         onTriggerClick : function(e)
42870         {
42871             Roo.log('trigger click');
42872             if(this.disabled){
42873                 return;
42874             }
42875             
42876             if(this.isExpanded()){
42877                 this.collapse();
42878                 this.hasFocus = false;
42879             }else {
42880                 this.store.load({});
42881                 this.hasFocus = true;
42882                 this.expand();
42883             }
42884         },
42885         
42886         isExpanded : function()
42887         {
42888             return this.list.isVisible();
42889         },
42890         
42891         collapse : function()
42892         {
42893             if(!this.isExpanded()){
42894                 return;
42895             }
42896             this.list.hide();
42897             Roo.get(document).un('mousedown', this.collapseIf, this);
42898             Roo.get(document).un('mousewheel', this.collapseIf, this);
42899             this.fireEvent('collapse', this);
42900             this.validate();
42901         },
42902         
42903         expand : function()
42904         {
42905             Roo.log('expand');
42906
42907             if(this.isExpanded() || !this.hasFocus){
42908                 return;
42909             }
42910             
42911             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42912             this.list.setWidth(lw);
42913             
42914             this.list.show();
42915             this.restrictHeight();
42916             
42917             Roo.get(document).on('mousedown', this.collapseIf, this);
42918             Roo.get(document).on('mousewheel', this.collapseIf, this);
42919             
42920             this.fireEvent('expand', this);
42921         },
42922         
42923         restrictHeight : function()
42924         {
42925             this.list.alignTo(this.inputEl(), this.listAlign);
42926             this.list.alignTo(this.inputEl(), this.listAlign);
42927         },
42928         
42929         onViewOver : function(e, t)
42930         {
42931             if(this.inKeyMode){
42932                 return;
42933             }
42934             var item = this.view.findItemFromChild(t);
42935             
42936             if(item){
42937                 var index = this.view.indexOf(item);
42938                 this.select(index, false);
42939             }
42940         },
42941
42942         // private
42943         onViewClick : function(view, doFocus, el, e)
42944         {
42945             var index = this.view.getSelectedIndexes()[0];
42946             
42947             var r = this.store.getAt(index);
42948             
42949             if(r){
42950                 this.onSelect(r, index);
42951             }
42952             if(doFocus !== false && !this.blockFocus){
42953                 this.inputEl().focus();
42954             }
42955         },
42956         
42957         onViewMove : function(e, t)
42958         {
42959             this.inKeyMode = false;
42960         },
42961         
42962         select : function(index, scrollIntoView)
42963         {
42964             this.selectedIndex = index;
42965             this.view.select(index);
42966             if(scrollIntoView !== false){
42967                 var el = this.view.getNode(index);
42968                 if(el){
42969                     this.list.scrollChildIntoView(el, false);
42970                 }
42971             }
42972         },
42973         
42974         createList : function()
42975         {
42976             this.list = Roo.get(document.body).createChild({
42977                 tag: 'ul',
42978                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42979                 style: 'display:none'
42980             });
42981             
42982             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42983         },
42984         
42985         collapseIf : function(e)
42986         {
42987             var in_combo  = e.within(this.el);
42988             var in_list =  e.within(this.list);
42989             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42990             
42991             if (in_combo || in_list || is_list) {
42992                 return;
42993             }
42994             this.collapse();
42995         },
42996         
42997         onSelect : function(record, index)
42998         {
42999             if(this.fireEvent('beforeselect', this, record, index) !== false){
43000                 
43001                 this.setFlagClass(record.data.iso2);
43002                 this.setDialCode(record.data.dialCode);
43003                 this.hasFocus = false;
43004                 this.collapse();
43005                 this.fireEvent('select', this, record, index);
43006             }
43007         },
43008         
43009         flagEl : function()
43010         {
43011             var flag = this.el.select('div.flag',true).first();
43012             if(!flag){
43013                 return false;
43014             }
43015             return flag;
43016         },
43017         
43018         dialCodeHolderEl : function()
43019         {
43020             var d = this.el.select('input.dial-code-holder',true).first();
43021             if(!d){
43022                 return false;
43023             }
43024             return d;
43025         },
43026         
43027         setDialCode : function(v)
43028         {
43029             this.dialCodeHolder.dom.value = '+'+v;
43030         },
43031         
43032         setFlagClass : function(n)
43033         {
43034             this.flag.dom.className = 'flag '+n;
43035         },
43036         
43037         getValue : function()
43038         {
43039             var v = this.inputEl().getValue();
43040             if(this.dialCodeHolder) {
43041                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43042             }
43043             return v;
43044         },
43045         
43046         setValue : function(v)
43047         {
43048             var d = this.getDialCode(v);
43049             
43050             //invalid dial code
43051             if(v.length == 0 || !d || d.length == 0) {
43052                 if(this.rendered){
43053                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43054                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43055                 }
43056                 return;
43057             }
43058             
43059             //valid dial code
43060             this.setFlagClass(this.dialCodeMapping[d].iso2);
43061             this.setDialCode(d);
43062             this.inputEl().dom.value = v.replace('+'+d,'');
43063             this.hiddenEl().dom.value = this.getValue();
43064             
43065             this.validate();
43066         },
43067         
43068         getDialCode : function(v)
43069         {
43070             v = v ||  '';
43071             
43072             if (v.length == 0) {
43073                 return this.dialCodeHolder.dom.value;
43074             }
43075             
43076             var dialCode = "";
43077             if (v.charAt(0) != "+") {
43078                 return false;
43079             }
43080             var numericChars = "";
43081             for (var i = 1; i < v.length; i++) {
43082               var c = v.charAt(i);
43083               if (!isNaN(c)) {
43084                 numericChars += c;
43085                 if (this.dialCodeMapping[numericChars]) {
43086                   dialCode = v.substr(1, i);
43087                 }
43088                 if (numericChars.length == 4) {
43089                   break;
43090                 }
43091               }
43092             }
43093             return dialCode;
43094         },
43095         
43096         reset : function()
43097         {
43098             this.setValue(this.defaultDialCode);
43099             this.validate();
43100         },
43101         
43102         hiddenEl : function()
43103         {
43104             return this.el.select('input.hidden-tel-input',true).first();
43105         },
43106         
43107         // after setting val
43108         onKeyUp : function(e){
43109             this.setValue(this.getValue());
43110         },
43111         
43112         onKeyPress : function(e){
43113             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43114                 e.stopEvent();
43115             }
43116         }
43117         
43118 });
43119 /**
43120  * @class Roo.bootstrap.MoneyField
43121  * @extends Roo.bootstrap.ComboBox
43122  * Bootstrap MoneyField class
43123  * 
43124  * @constructor
43125  * Create a new MoneyField.
43126  * @param {Object} config Configuration options
43127  */
43128
43129 Roo.bootstrap.MoneyField = function(config) {
43130     
43131     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43132     
43133 };
43134
43135 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43136     
43137     /**
43138      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43139      */
43140     allowDecimals : true,
43141     /**
43142      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43143      */
43144     decimalSeparator : ".",
43145     /**
43146      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43147      */
43148     decimalPrecision : 0,
43149     /**
43150      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43151      */
43152     allowNegative : true,
43153     /**
43154      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43155      */
43156     allowZero: true,
43157     /**
43158      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43159      */
43160     minValue : Number.NEGATIVE_INFINITY,
43161     /**
43162      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43163      */
43164     maxValue : Number.MAX_VALUE,
43165     /**
43166      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43167      */
43168     minText : "The minimum value for this field is {0}",
43169     /**
43170      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43171      */
43172     maxText : "The maximum value for this field is {0}",
43173     /**
43174      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43175      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43176      */
43177     nanText : "{0} is not a valid number",
43178     /**
43179      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43180      */
43181     castInt : true,
43182     /**
43183      * @cfg {String} defaults currency of the MoneyField
43184      * value should be in lkey
43185      */
43186     defaultCurrency : false,
43187     /**
43188      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43189      */
43190     thousandsDelimiter : false,
43191     /**
43192      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43193      */
43194     max_length: false,
43195     
43196     inputlg : 9,
43197     inputmd : 9,
43198     inputsm : 9,
43199     inputxs : 6,
43200     
43201     store : false,
43202     
43203     getAutoCreate : function()
43204     {
43205         var align = this.labelAlign || this.parentLabelAlign();
43206         
43207         var id = Roo.id();
43208
43209         var cfg = {
43210             cls: 'form-group',
43211             cn: []
43212         };
43213
43214         var input =  {
43215             tag: 'input',
43216             id : id,
43217             cls : 'form-control roo-money-amount-input',
43218             autocomplete: 'new-password'
43219         };
43220         
43221         var hiddenInput = {
43222             tag: 'input',
43223             type: 'hidden',
43224             id: Roo.id(),
43225             cls: 'hidden-number-input'
43226         };
43227         
43228         if(this.max_length) {
43229             input.maxlength = this.max_length; 
43230         }
43231         
43232         if (this.name) {
43233             hiddenInput.name = this.name;
43234         }
43235
43236         if (this.disabled) {
43237             input.disabled = true;
43238         }
43239
43240         var clg = 12 - this.inputlg;
43241         var cmd = 12 - this.inputmd;
43242         var csm = 12 - this.inputsm;
43243         var cxs = 12 - this.inputxs;
43244         
43245         var container = {
43246             tag : 'div',
43247             cls : 'row roo-money-field',
43248             cn : [
43249                 {
43250                     tag : 'div',
43251                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43252                     cn : [
43253                         {
43254                             tag : 'div',
43255                             cls: 'roo-select2-container input-group',
43256                             cn: [
43257                                 {
43258                                     tag : 'input',
43259                                     cls : 'form-control roo-money-currency-input',
43260                                     autocomplete: 'new-password',
43261                                     readOnly : 1,
43262                                     name : this.currencyName
43263                                 },
43264                                 {
43265                                     tag :'span',
43266                                     cls : 'input-group-addon',
43267                                     cn : [
43268                                         {
43269                                             tag: 'span',
43270                                             cls: 'caret'
43271                                         }
43272                                     ]
43273                                 }
43274                             ]
43275                         }
43276                     ]
43277                 },
43278                 {
43279                     tag : 'div',
43280                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43281                     cn : [
43282                         {
43283                             tag: 'div',
43284                             cls: this.hasFeedback ? 'has-feedback' : '',
43285                             cn: [
43286                                 input
43287                             ]
43288                         }
43289                     ]
43290                 }
43291             ]
43292             
43293         };
43294         
43295         if (this.fieldLabel.length) {
43296             var indicator = {
43297                 tag: 'i',
43298                 tooltip: 'This field is required'
43299             };
43300
43301             var label = {
43302                 tag: 'label',
43303                 'for':  id,
43304                 cls: 'control-label',
43305                 cn: []
43306             };
43307
43308             var label_text = {
43309                 tag: 'span',
43310                 html: this.fieldLabel
43311             };
43312
43313             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43314             label.cn = [
43315                 indicator,
43316                 label_text
43317             ];
43318
43319             if(this.indicatorpos == 'right') {
43320                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43321                 label.cn = [
43322                     label_text,
43323                     indicator
43324                 ];
43325             }
43326
43327             if(align == 'left') {
43328                 container = {
43329                     tag: 'div',
43330                     cn: [
43331                         container
43332                     ]
43333                 };
43334
43335                 if(this.labelWidth > 12){
43336                     label.style = "width: " + this.labelWidth + 'px';
43337                 }
43338                 if(this.labelWidth < 13 && this.labelmd == 0){
43339                     this.labelmd = this.labelWidth;
43340                 }
43341                 if(this.labellg > 0){
43342                     label.cls += ' col-lg-' + this.labellg;
43343                     input.cls += ' col-lg-' + (12 - this.labellg);
43344                 }
43345                 if(this.labelmd > 0){
43346                     label.cls += ' col-md-' + this.labelmd;
43347                     container.cls += ' col-md-' + (12 - this.labelmd);
43348                 }
43349                 if(this.labelsm > 0){
43350                     label.cls += ' col-sm-' + this.labelsm;
43351                     container.cls += ' col-sm-' + (12 - this.labelsm);
43352                 }
43353                 if(this.labelxs > 0){
43354                     label.cls += ' col-xs-' + this.labelxs;
43355                     container.cls += ' col-xs-' + (12 - this.labelxs);
43356                 }
43357             }
43358         }
43359
43360         cfg.cn = [
43361             label,
43362             container,
43363             hiddenInput
43364         ];
43365         
43366         var settings = this;
43367
43368         ['xs','sm','md','lg'].map(function(size){
43369             if (settings[size]) {
43370                 cfg.cls += ' col-' + size + '-' + settings[size];
43371             }
43372         });
43373         
43374         return cfg;
43375     },
43376     
43377     initEvents : function()
43378     {
43379         this.indicator = this.indicatorEl();
43380         
43381         this.initCurrencyEvent();
43382         
43383         this.initNumberEvent();
43384     },
43385     
43386     initCurrencyEvent : function()
43387     {
43388         if (!this.store) {
43389             throw "can not find store for combo";
43390         }
43391         
43392         this.store = Roo.factory(this.store, Roo.data);
43393         this.store.parent = this;
43394         
43395         this.createList();
43396         
43397         this.triggerEl = this.el.select('.input-group-addon', true).first();
43398         
43399         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43400         
43401         var _this = this;
43402         
43403         (function(){
43404             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43405             _this.list.setWidth(lw);
43406         }).defer(100);
43407         
43408         this.list.on('mouseover', this.onViewOver, this);
43409         this.list.on('mousemove', this.onViewMove, this);
43410         this.list.on('scroll', this.onViewScroll, this);
43411         
43412         if(!this.tpl){
43413             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43414         }
43415         
43416         this.view = new Roo.View(this.list, this.tpl, {
43417             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43418         });
43419         
43420         this.view.on('click', this.onViewClick, this);
43421         
43422         this.store.on('beforeload', this.onBeforeLoad, this);
43423         this.store.on('load', this.onLoad, this);
43424         this.store.on('loadexception', this.onLoadException, this);
43425         
43426         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43427             "up" : function(e){
43428                 this.inKeyMode = true;
43429                 this.selectPrev();
43430             },
43431
43432             "down" : function(e){
43433                 if(!this.isExpanded()){
43434                     this.onTriggerClick();
43435                 }else{
43436                     this.inKeyMode = true;
43437                     this.selectNext();
43438                 }
43439             },
43440
43441             "enter" : function(e){
43442                 this.collapse();
43443                 
43444                 if(this.fireEvent("specialkey", this, e)){
43445                     this.onViewClick(false);
43446                 }
43447                 
43448                 return true;
43449             },
43450
43451             "esc" : function(e){
43452                 this.collapse();
43453             },
43454
43455             "tab" : function(e){
43456                 this.collapse();
43457                 
43458                 if(this.fireEvent("specialkey", this, e)){
43459                     this.onViewClick(false);
43460                 }
43461                 
43462                 return true;
43463             },
43464
43465             scope : this,
43466
43467             doRelay : function(foo, bar, hname){
43468                 if(hname == 'down' || this.scope.isExpanded()){
43469                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43470                 }
43471                 return true;
43472             },
43473
43474             forceKeyDown: true
43475         });
43476         
43477         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43478         
43479     },
43480     
43481     initNumberEvent : function(e)
43482     {
43483         this.inputEl().on("keydown" , this.fireKey,  this);
43484         this.inputEl().on("focus", this.onFocus,  this);
43485         this.inputEl().on("blur", this.onBlur,  this);
43486         
43487         this.inputEl().relayEvent('keyup', this);
43488         
43489         if(this.indicator){
43490             this.indicator.addClass('invisible');
43491         }
43492  
43493         this.originalValue = this.getValue();
43494         
43495         if(this.validationEvent == 'keyup'){
43496             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43497             this.inputEl().on('keyup', this.filterValidation, this);
43498         }
43499         else if(this.validationEvent !== false){
43500             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43501         }
43502         
43503         if(this.selectOnFocus){
43504             this.on("focus", this.preFocus, this);
43505             
43506         }
43507         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43508             this.inputEl().on("keypress", this.filterKeys, this);
43509         } else {
43510             this.inputEl().relayEvent('keypress', this);
43511         }
43512         
43513         var allowed = "0123456789";
43514         
43515         if(this.allowDecimals){
43516             allowed += this.decimalSeparator;
43517         }
43518         
43519         if(this.allowNegative){
43520             allowed += "-";
43521         }
43522         
43523         if(this.thousandsDelimiter) {
43524             allowed += ",";
43525         }
43526         
43527         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43528         
43529         var keyPress = function(e){
43530             
43531             var k = e.getKey();
43532             
43533             var c = e.getCharCode();
43534             
43535             if(
43536                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43537                     allowed.indexOf(String.fromCharCode(c)) === -1
43538             ){
43539                 e.stopEvent();
43540                 return;
43541             }
43542             
43543             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43544                 return;
43545             }
43546             
43547             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43548                 e.stopEvent();
43549             }
43550         };
43551         
43552         this.inputEl().on("keypress", keyPress, this);
43553         
43554     },
43555     
43556     onTriggerClick : function(e)
43557     {   
43558         if(this.disabled){
43559             return;
43560         }
43561         
43562         this.page = 0;
43563         this.loadNext = false;
43564         
43565         if(this.isExpanded()){
43566             this.collapse();
43567             return;
43568         }
43569         
43570         this.hasFocus = true;
43571         
43572         if(this.triggerAction == 'all') {
43573             this.doQuery(this.allQuery, true);
43574             return;
43575         }
43576         
43577         this.doQuery(this.getRawValue());
43578     },
43579     
43580     getCurrency : function()
43581     {   
43582         var v = this.currencyEl().getValue();
43583         
43584         return v;
43585     },
43586     
43587     restrictHeight : function()
43588     {
43589         this.list.alignTo(this.currencyEl(), this.listAlign);
43590         this.list.alignTo(this.currencyEl(), this.listAlign);
43591     },
43592     
43593     onViewClick : function(view, doFocus, el, e)
43594     {
43595         var index = this.view.getSelectedIndexes()[0];
43596         
43597         var r = this.store.getAt(index);
43598         
43599         if(r){
43600             this.onSelect(r, index);
43601         }
43602     },
43603     
43604     onSelect : function(record, index){
43605         
43606         if(this.fireEvent('beforeselect', this, record, index) !== false){
43607         
43608             this.setFromCurrencyData(index > -1 ? record.data : false);
43609             
43610             this.collapse();
43611             
43612             this.fireEvent('select', this, record, index);
43613         }
43614     },
43615     
43616     setFromCurrencyData : function(o)
43617     {
43618         var currency = '';
43619         
43620         this.lastCurrency = o;
43621         
43622         if (this.currencyField) {
43623             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43624         } else {
43625             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43626         }
43627         
43628         this.lastSelectionText = currency;
43629         
43630         //setting default currency
43631         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43632             this.setCurrency(this.defaultCurrency);
43633             return;
43634         }
43635         
43636         this.setCurrency(currency);
43637     },
43638     
43639     setFromData : function(o)
43640     {
43641         var c = {};
43642         
43643         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43644         
43645         this.setFromCurrencyData(c);
43646         
43647         var value = '';
43648         
43649         if (this.name) {
43650             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43651         } else {
43652             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43653         }
43654         
43655         this.setValue(value);
43656         
43657     },
43658     
43659     setCurrency : function(v)
43660     {   
43661         this.currencyValue = v;
43662         
43663         if(this.rendered){
43664             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43665             this.validate();
43666         }
43667     },
43668     
43669     setValue : function(v)
43670     {
43671         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43672         
43673         this.value = v;
43674         
43675         if(this.rendered){
43676             
43677             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43678             
43679             this.inputEl().dom.value = (v == '') ? '' :
43680                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43681             
43682             if(!this.allowZero && v === '0') {
43683                 this.hiddenEl().dom.value = '';
43684                 this.inputEl().dom.value = '';
43685             }
43686             
43687             this.validate();
43688         }
43689     },
43690     
43691     getRawValue : function()
43692     {
43693         var v = this.inputEl().getValue();
43694         
43695         return v;
43696     },
43697     
43698     getValue : function()
43699     {
43700         return this.fixPrecision(this.parseValue(this.getRawValue()));
43701     },
43702     
43703     parseValue : function(value)
43704     {
43705         if(this.thousandsDelimiter) {
43706             value += "";
43707             r = new RegExp(",", "g");
43708             value = value.replace(r, "");
43709         }
43710         
43711         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43712         return isNaN(value) ? '' : value;
43713         
43714     },
43715     
43716     fixPrecision : function(value)
43717     {
43718         if(this.thousandsDelimiter) {
43719             value += "";
43720             r = new RegExp(",", "g");
43721             value = value.replace(r, "");
43722         }
43723         
43724         var nan = isNaN(value);
43725         
43726         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43727             return nan ? '' : value;
43728         }
43729         return parseFloat(value).toFixed(this.decimalPrecision);
43730     },
43731     
43732     decimalPrecisionFcn : function(v)
43733     {
43734         return Math.floor(v);
43735     },
43736     
43737     validateValue : function(value)
43738     {
43739         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43740             return false;
43741         }
43742         
43743         var num = this.parseValue(value);
43744         
43745         if(isNaN(num)){
43746             this.markInvalid(String.format(this.nanText, value));
43747             return false;
43748         }
43749         
43750         if(num < this.minValue){
43751             this.markInvalid(String.format(this.minText, this.minValue));
43752             return false;
43753         }
43754         
43755         if(num > this.maxValue){
43756             this.markInvalid(String.format(this.maxText, this.maxValue));
43757             return false;
43758         }
43759         
43760         return true;
43761     },
43762     
43763     validate : function()
43764     {
43765         if(this.disabled || this.allowBlank){
43766             this.markValid();
43767             return true;
43768         }
43769         
43770         var currency = this.getCurrency();
43771         
43772         if(this.validateValue(this.getRawValue()) && currency.length){
43773             this.markValid();
43774             return true;
43775         }
43776         
43777         this.markInvalid();
43778         return false;
43779     },
43780     
43781     getName: function()
43782     {
43783         return this.name;
43784     },
43785     
43786     beforeBlur : function()
43787     {
43788         if(!this.castInt){
43789             return;
43790         }
43791         
43792         var v = this.parseValue(this.getRawValue());
43793         
43794         if(v || v == 0){
43795             this.setValue(v);
43796         }
43797     },
43798     
43799     onBlur : function()
43800     {
43801         this.beforeBlur();
43802         
43803         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43804             //this.el.removeClass(this.focusClass);
43805         }
43806         
43807         this.hasFocus = false;
43808         
43809         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43810             this.validate();
43811         }
43812         
43813         var v = this.getValue();
43814         
43815         if(String(v) !== String(this.startValue)){
43816             this.fireEvent('change', this, v, this.startValue);
43817         }
43818         
43819         this.fireEvent("blur", this);
43820     },
43821     
43822     inputEl : function()
43823     {
43824         return this.el.select('.roo-money-amount-input', true).first();
43825     },
43826     
43827     currencyEl : function()
43828     {
43829         return this.el.select('.roo-money-currency-input', true).first();
43830     },
43831     
43832     hiddenEl : function()
43833     {
43834         return this.el.select('input.hidden-number-input',true).first();
43835     }
43836     
43837 });/**
43838  * @class Roo.bootstrap.BezierSignature
43839  * @extends Roo.bootstrap.Component
43840  * Bootstrap BezierSignature class
43841  * This script refer to:
43842  *    Title: Signature Pad
43843  *    Author: szimek
43844  *    Availability: https://github.com/szimek/signature_pad
43845  *
43846  * @constructor
43847  * Create a new BezierSignature
43848  * @param {Object} config The config object
43849  */
43850
43851 Roo.bootstrap.BezierSignature = function(config){
43852     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43853     this.addEvents({
43854         "resize" : true
43855     });
43856 };
43857
43858 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43859 {
43860      
43861     curve_data: [],
43862     
43863     is_empty: true,
43864     
43865     mouse_btn_down: true,
43866     
43867     /**
43868      * @cfg {int} canvas height
43869      */
43870     canvas_height: '200px',
43871     
43872     /**
43873      * @cfg {float|function} Radius of a single dot.
43874      */ 
43875     dot_size: false,
43876     
43877     /**
43878      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43879      */
43880     min_width: 0.5,
43881     
43882     /**
43883      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43884      */
43885     max_width: 2.5,
43886     
43887     /**
43888      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43889      */
43890     throttle: 16,
43891     
43892     /**
43893      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43894      */
43895     min_distance: 5,
43896     
43897     /**
43898      * @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.
43899      */
43900     bg_color: 'rgba(0, 0, 0, 0)',
43901     
43902     /**
43903      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43904      */
43905     dot_color: 'black',
43906     
43907     /**
43908      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43909      */ 
43910     velocity_filter_weight: 0.7,
43911     
43912     /**
43913      * @cfg {function} Callback when stroke begin. 
43914      */
43915     onBegin: false,
43916     
43917     /**
43918      * @cfg {function} Callback when stroke end.
43919      */
43920     onEnd: false,
43921     
43922     getAutoCreate : function()
43923     {
43924         var cls = 'roo-signature column';
43925         
43926         if(this.cls){
43927             cls += ' ' + this.cls;
43928         }
43929         
43930         var col_sizes = [
43931             'lg',
43932             'md',
43933             'sm',
43934             'xs'
43935         ];
43936         
43937         for(var i = 0; i < col_sizes.length; i++) {
43938             if(this[col_sizes[i]]) {
43939                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43940             }
43941         }
43942         
43943         var cfg = {
43944             tag: 'div',
43945             cls: cls,
43946             cn: [
43947                 {
43948                     tag: 'div',
43949                     cls: 'roo-signature-body',
43950                     cn: [
43951                         {
43952                             tag: 'canvas',
43953                             cls: 'roo-signature-body-canvas',
43954                             height: this.canvas_height,
43955                             width: this.canvas_width
43956                         }
43957                     ]
43958                 },
43959                 {
43960                     tag: 'input',
43961                     type: 'file',
43962                     style: 'display: none'
43963                 }
43964             ]
43965         };
43966         
43967         return cfg;
43968     },
43969     
43970     initEvents: function() 
43971     {
43972         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43973         
43974         var canvas = this.canvasEl();
43975         
43976         // mouse && touch event swapping...
43977         canvas.dom.style.touchAction = 'none';
43978         canvas.dom.style.msTouchAction = 'none';
43979         
43980         this.mouse_btn_down = false;
43981         canvas.on('mousedown', this._handleMouseDown, this);
43982         canvas.on('mousemove', this._handleMouseMove, this);
43983         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43984         
43985         if (window.PointerEvent) {
43986             canvas.on('pointerdown', this._handleMouseDown, this);
43987             canvas.on('pointermove', this._handleMouseMove, this);
43988             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43989         }
43990         
43991         if ('ontouchstart' in window) {
43992             canvas.on('touchstart', this._handleTouchStart, this);
43993             canvas.on('touchmove', this._handleTouchMove, this);
43994             canvas.on('touchend', this._handleTouchEnd, this);
43995         }
43996         
43997         Roo.EventManager.onWindowResize(this.resize, this, true);
43998         
43999         // file input event
44000         this.fileEl().on('change', this.uploadImage, this);
44001         
44002         this.clear();
44003         
44004         this.resize();
44005     },
44006     
44007     resize: function(){
44008         
44009         var canvas = this.canvasEl().dom;
44010         var ctx = this.canvasElCtx();
44011         var img_data = false;
44012         
44013         if(canvas.width > 0) {
44014             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44015         }
44016         // setting canvas width will clean img data
44017         canvas.width = 0;
44018         
44019         var style = window.getComputedStyle ? 
44020             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44021             
44022         var padding_left = parseInt(style.paddingLeft) || 0;
44023         var padding_right = parseInt(style.paddingRight) || 0;
44024         
44025         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44026         
44027         if(img_data) {
44028             ctx.putImageData(img_data, 0, 0);
44029         }
44030     },
44031     
44032     _handleMouseDown: function(e)
44033     {
44034         if (e.browserEvent.which === 1) {
44035             this.mouse_btn_down = true;
44036             this.strokeBegin(e);
44037         }
44038     },
44039     
44040     _handleMouseMove: function (e)
44041     {
44042         if (this.mouse_btn_down) {
44043             this.strokeMoveUpdate(e);
44044         }
44045     },
44046     
44047     _handleMouseUp: function (e)
44048     {
44049         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44050             this.mouse_btn_down = false;
44051             this.strokeEnd(e);
44052         }
44053     },
44054     
44055     _handleTouchStart: function (e) {
44056         
44057         e.preventDefault();
44058         if (e.browserEvent.targetTouches.length === 1) {
44059             // var touch = e.browserEvent.changedTouches[0];
44060             // this.strokeBegin(touch);
44061             
44062              this.strokeBegin(e); // assume e catching the correct xy...
44063         }
44064     },
44065     
44066     _handleTouchMove: function (e) {
44067         e.preventDefault();
44068         // var touch = event.targetTouches[0];
44069         // _this._strokeMoveUpdate(touch);
44070         this.strokeMoveUpdate(e);
44071     },
44072     
44073     _handleTouchEnd: function (e) {
44074         var wasCanvasTouched = e.target === this.canvasEl().dom;
44075         if (wasCanvasTouched) {
44076             e.preventDefault();
44077             // var touch = event.changedTouches[0];
44078             // _this._strokeEnd(touch);
44079             this.strokeEnd(e);
44080         }
44081     },
44082     
44083     reset: function () {
44084         this._lastPoints = [];
44085         this._lastVelocity = 0;
44086         this._lastWidth = (this.min_width + this.max_width) / 2;
44087         this.canvasElCtx().fillStyle = this.dot_color;
44088     },
44089     
44090     strokeMoveUpdate: function(e)
44091     {
44092         this.strokeUpdate(e);
44093         
44094         if (this.throttle) {
44095             this.throttleStroke(this.strokeUpdate, this.throttle);
44096         }
44097         else {
44098             this.strokeUpdate(e);
44099         }
44100     },
44101     
44102     strokeBegin: function(e)
44103     {
44104         var newPointGroup = {
44105             color: this.dot_color,
44106             points: []
44107         };
44108         
44109         if (typeof this.onBegin === 'function') {
44110             this.onBegin(e);
44111         }
44112         
44113         this.curve_data.push(newPointGroup);
44114         this.reset();
44115         this.strokeUpdate(e);
44116     },
44117     
44118     strokeUpdate: function(e)
44119     {
44120         var rect = this.canvasEl().dom.getBoundingClientRect();
44121         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44122         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44123         var lastPoints = lastPointGroup.points;
44124         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44125         var isLastPointTooClose = lastPoint
44126             ? point.distanceTo(lastPoint) <= this.min_distance
44127             : false;
44128         var color = lastPointGroup.color;
44129         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44130             var curve = this.addPoint(point);
44131             if (!lastPoint) {
44132                 this.drawDot({color: color, point: point});
44133             }
44134             else if (curve) {
44135                 this.drawCurve({color: color, curve: curve});
44136             }
44137             lastPoints.push({
44138                 time: point.time,
44139                 x: point.x,
44140                 y: point.y
44141             });
44142         }
44143     },
44144     
44145     strokeEnd: function(e)
44146     {
44147         this.strokeUpdate(e);
44148         if (typeof this.onEnd === 'function') {
44149             this.onEnd(e);
44150         }
44151     },
44152     
44153     addPoint:  function (point) {
44154         var _lastPoints = this._lastPoints;
44155         _lastPoints.push(point);
44156         if (_lastPoints.length > 2) {
44157             if (_lastPoints.length === 3) {
44158                 _lastPoints.unshift(_lastPoints[0]);
44159             }
44160             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44161             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44162             _lastPoints.shift();
44163             return curve;
44164         }
44165         return null;
44166     },
44167     
44168     calculateCurveWidths: function (startPoint, endPoint) {
44169         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44170             (1 - this.velocity_filter_weight) * this._lastVelocity;
44171
44172         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44173         var widths = {
44174             end: newWidth,
44175             start: this._lastWidth
44176         };
44177         
44178         this._lastVelocity = velocity;
44179         this._lastWidth = newWidth;
44180         return widths;
44181     },
44182     
44183     drawDot: function (_a) {
44184         var color = _a.color, point = _a.point;
44185         var ctx = this.canvasElCtx();
44186         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44187         ctx.beginPath();
44188         this.drawCurveSegment(point.x, point.y, width);
44189         ctx.closePath();
44190         ctx.fillStyle = color;
44191         ctx.fill();
44192     },
44193     
44194     drawCurve: function (_a) {
44195         var color = _a.color, curve = _a.curve;
44196         var ctx = this.canvasElCtx();
44197         var widthDelta = curve.endWidth - curve.startWidth;
44198         var drawSteps = Math.floor(curve.length()) * 2;
44199         ctx.beginPath();
44200         ctx.fillStyle = color;
44201         for (var i = 0; i < drawSteps; i += 1) {
44202         var t = i / drawSteps;
44203         var tt = t * t;
44204         var ttt = tt * t;
44205         var u = 1 - t;
44206         var uu = u * u;
44207         var uuu = uu * u;
44208         var x = uuu * curve.startPoint.x;
44209         x += 3 * uu * t * curve.control1.x;
44210         x += 3 * u * tt * curve.control2.x;
44211         x += ttt * curve.endPoint.x;
44212         var y = uuu * curve.startPoint.y;
44213         y += 3 * uu * t * curve.control1.y;
44214         y += 3 * u * tt * curve.control2.y;
44215         y += ttt * curve.endPoint.y;
44216         var width = curve.startWidth + ttt * widthDelta;
44217         this.drawCurveSegment(x, y, width);
44218         }
44219         ctx.closePath();
44220         ctx.fill();
44221     },
44222     
44223     drawCurveSegment: function (x, y, width) {
44224         var ctx = this.canvasElCtx();
44225         ctx.moveTo(x, y);
44226         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44227         this.is_empty = false;
44228     },
44229     
44230     clear: function()
44231     {
44232         var ctx = this.canvasElCtx();
44233         var canvas = this.canvasEl().dom;
44234         ctx.fillStyle = this.bg_color;
44235         ctx.clearRect(0, 0, canvas.width, canvas.height);
44236         ctx.fillRect(0, 0, canvas.width, canvas.height);
44237         this.curve_data = [];
44238         this.reset();
44239         this.is_empty = true;
44240     },
44241     
44242     fileEl: function()
44243     {
44244         return  this.el.select('input',true).first();
44245     },
44246     
44247     canvasEl: function()
44248     {
44249         return this.el.select('canvas',true).first();
44250     },
44251     
44252     canvasElCtx: function()
44253     {
44254         return this.el.select('canvas',true).first().dom.getContext('2d');
44255     },
44256     
44257     getImage: function(type)
44258     {
44259         if(this.is_empty) {
44260             return false;
44261         }
44262         
44263         // encryption ?
44264         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44265     },
44266     
44267     drawFromImage: function(img_src)
44268     {
44269         var img = new Image();
44270         
44271         img.onload = function(){
44272             this.canvasElCtx().drawImage(img, 0, 0);
44273         }.bind(this);
44274         
44275         img.src = img_src;
44276         
44277         this.is_empty = false;
44278     },
44279     
44280     selectImage: function()
44281     {
44282         this.fileEl().dom.click();
44283     },
44284     
44285     uploadImage: function(e)
44286     {
44287         var reader = new FileReader();
44288         
44289         reader.onload = function(e){
44290             var img = new Image();
44291             img.onload = function(){
44292                 this.reset();
44293                 this.canvasElCtx().drawImage(img, 0, 0);
44294             }.bind(this);
44295             img.src = e.target.result;
44296         }.bind(this);
44297         
44298         reader.readAsDataURL(e.target.files[0]);
44299     },
44300     
44301     // Bezier Point Constructor
44302     Point: (function () {
44303         function Point(x, y, time) {
44304             this.x = x;
44305             this.y = y;
44306             this.time = time || Date.now();
44307         }
44308         Point.prototype.distanceTo = function (start) {
44309             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44310         };
44311         Point.prototype.equals = function (other) {
44312             return this.x === other.x && this.y === other.y && this.time === other.time;
44313         };
44314         Point.prototype.velocityFrom = function (start) {
44315             return this.time !== start.time
44316             ? this.distanceTo(start) / (this.time - start.time)
44317             : 0;
44318         };
44319         return Point;
44320     }()),
44321     
44322     
44323     // Bezier Constructor
44324     Bezier: (function () {
44325         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44326             this.startPoint = startPoint;
44327             this.control2 = control2;
44328             this.control1 = control1;
44329             this.endPoint = endPoint;
44330             this.startWidth = startWidth;
44331             this.endWidth = endWidth;
44332         }
44333         Bezier.fromPoints = function (points, widths, scope) {
44334             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44335             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44336             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44337         };
44338         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44339             var dx1 = s1.x - s2.x;
44340             var dy1 = s1.y - s2.y;
44341             var dx2 = s2.x - s3.x;
44342             var dy2 = s2.y - s3.y;
44343             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44344             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44345             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44346             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44347             var dxm = m1.x - m2.x;
44348             var dym = m1.y - m2.y;
44349             var k = l2 / (l1 + l2);
44350             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44351             var tx = s2.x - cm.x;
44352             var ty = s2.y - cm.y;
44353             return {
44354                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44355                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44356             };
44357         };
44358         Bezier.prototype.length = function () {
44359             var steps = 10;
44360             var length = 0;
44361             var px;
44362             var py;
44363             for (var i = 0; i <= steps; i += 1) {
44364                 var t = i / steps;
44365                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44366                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44367                 if (i > 0) {
44368                     var xdiff = cx - px;
44369                     var ydiff = cy - py;
44370                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44371                 }
44372                 px = cx;
44373                 py = cy;
44374             }
44375             return length;
44376         };
44377         Bezier.prototype.point = function (t, start, c1, c2, end) {
44378             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44379             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44380             + (3.0 * c2 * (1.0 - t) * t * t)
44381             + (end * t * t * t);
44382         };
44383         return Bezier;
44384     }()),
44385     
44386     throttleStroke: function(fn, wait) {
44387       if (wait === void 0) { wait = 250; }
44388       var previous = 0;
44389       var timeout = null;
44390       var result;
44391       var storedContext;
44392       var storedArgs;
44393       var later = function () {
44394           previous = Date.now();
44395           timeout = null;
44396           result = fn.apply(storedContext, storedArgs);
44397           if (!timeout) {
44398               storedContext = null;
44399               storedArgs = [];
44400           }
44401       };
44402       return function wrapper() {
44403           var args = [];
44404           for (var _i = 0; _i < arguments.length; _i++) {
44405               args[_i] = arguments[_i];
44406           }
44407           var now = Date.now();
44408           var remaining = wait - (now - previous);
44409           storedContext = this;
44410           storedArgs = args;
44411           if (remaining <= 0 || remaining > wait) {
44412               if (timeout) {
44413                   clearTimeout(timeout);
44414                   timeout = null;
44415               }
44416               previous = now;
44417               result = fn.apply(storedContext, storedArgs);
44418               if (!timeout) {
44419                   storedContext = null;
44420                   storedArgs = [];
44421               }
44422           }
44423           else if (!timeout) {
44424               timeout = window.setTimeout(later, remaining);
44425           }
44426           return result;
44427       };
44428   }
44429   
44430 });
44431
44432  
44433
44434