10d6464a8fa85264aa5f8e978fd99e01ef7789d9
[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 = (typeof(cfg.style) == 'undefined' ? this.style : 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     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310
7311 /**
7312  * @class Roo.grid.AbstractSelectionModel
7313  * @extends Roo.util.Observable
7314  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7315  * implemented by descendant classes.  This class should not be directly instantiated.
7316  * @constructor
7317  */
7318 Roo.grid.AbstractSelectionModel = function(){
7319     this.locked = false;
7320     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7321 };
7322
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7324     /** @ignore Called by the grid automatically. Do not call directly. */
7325     init : function(grid){
7326         this.grid = grid;
7327         this.initEvents();
7328     },
7329
7330     /**
7331      * Locks the selections.
7332      */
7333     lock : function(){
7334         this.locked = true;
7335     },
7336
7337     /**
7338      * Unlocks the selections.
7339      */
7340     unlock : function(){
7341         this.locked = false;
7342     },
7343
7344     /**
7345      * Returns true if the selections are locked.
7346      * @return {Boolean}
7347      */
7348     isLocked : function(){
7349         return this.locked;
7350     }
7351 });/*
7352  * Based on:
7353  * Ext JS Library 1.1.1
7354  * Copyright(c) 2006-2007, Ext JS, LLC.
7355  *
7356  * Originally Released Under LGPL - original licence link has changed is not relivant.
7357  *
7358  * Fork - LGPL
7359  * <script type="text/javascript">
7360  */
7361 /**
7362  * @extends Roo.grid.AbstractSelectionModel
7363  * @class Roo.grid.RowSelectionModel
7364  * The default SelectionModel used by {@link Roo.grid.Grid}.
7365  * It supports multiple selections and keyboard selection/navigation. 
7366  * @constructor
7367  * @param {Object} config
7368  */
7369 Roo.grid.RowSelectionModel = function(config){
7370     Roo.apply(this, config);
7371     this.selections = new Roo.util.MixedCollection(false, function(o){
7372         return o.id;
7373     });
7374
7375     this.last = false;
7376     this.lastActive = false;
7377
7378     this.addEvents({
7379         /**
7380         * @event selectionchange
7381         * Fires when the selection changes
7382         * @param {SelectionModel} this
7383         */
7384        "selectionchange" : true,
7385        /**
7386         * @event afterselectionchange
7387         * Fires after the selection changes (eg. by key press or clicking)
7388         * @param {SelectionModel} this
7389         */
7390        "afterselectionchange" : true,
7391        /**
7392         * @event beforerowselect
7393         * Fires when a row is selected being selected, return false to cancel.
7394         * @param {SelectionModel} this
7395         * @param {Number} rowIndex The selected index
7396         * @param {Boolean} keepExisting False if other selections will be cleared
7397         */
7398        "beforerowselect" : true,
7399        /**
7400         * @event rowselect
7401         * Fires when a row is selected.
7402         * @param {SelectionModel} this
7403         * @param {Number} rowIndex The selected index
7404         * @param {Roo.data.Record} r The record
7405         */
7406        "rowselect" : true,
7407        /**
7408         * @event rowdeselect
7409         * Fires when a row is deselected.
7410         * @param {SelectionModel} this
7411         * @param {Number} rowIndex The selected index
7412         */
7413         "rowdeselect" : true
7414     });
7415     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416     this.locked = false;
7417 };
7418
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7420     /**
7421      * @cfg {Boolean} singleSelect
7422      * True to allow selection of only one row at a time (defaults to false)
7423      */
7424     singleSelect : false,
7425
7426     // private
7427     initEvents : function(){
7428
7429         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430             this.grid.on("mousedown", this.handleMouseDown, this);
7431         }else{ // allow click to work like normal
7432             this.grid.on("rowclick", this.handleDragableRowClick, this);
7433         }
7434         // bootstrap does not have a view..
7435         var view = this.grid.view ? this.grid.view : this.grid;
7436         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7437             "up" : function(e){
7438                 if(!e.shiftKey){
7439                     this.selectPrevious(e.shiftKey);
7440                 }else if(this.last !== false && this.lastActive !== false){
7441                     var last = this.last;
7442                     this.selectRange(this.last,  this.lastActive-1);
7443                     view.focusRow(this.lastActive);
7444                     if(last !== false){
7445                         this.last = last;
7446                     }
7447                 }else{
7448                     this.selectFirstRow();
7449                 }
7450                 this.fireEvent("afterselectionchange", this);
7451             },
7452             "down" : function(e){
7453                 if(!e.shiftKey){
7454                     this.selectNext(e.shiftKey);
7455                 }else if(this.last !== false && this.lastActive !== false){
7456                     var last = this.last;
7457                     this.selectRange(this.last,  this.lastActive+1);
7458                     view.focusRow(this.lastActive);
7459                     if(last !== false){
7460                         this.last = last;
7461                     }
7462                 }else{
7463                     this.selectFirstRow();
7464                 }
7465                 this.fireEvent("afterselectionchange", this);
7466             },
7467             scope: this
7468         });
7469
7470          
7471         view.on("refresh", this.onRefresh, this);
7472         view.on("rowupdated", this.onRowUpdated, this);
7473         view.on("rowremoved", this.onRemove, this);
7474     },
7475
7476     // private
7477     onRefresh : function(){
7478         var ds = this.grid.dataSource, i, v = this.grid.view;
7479         var s = this.selections;
7480         s.each(function(r){
7481             if((i = ds.indexOfId(r.id)) != -1){
7482                 v.onRowSelect(i);
7483                 s.add(ds.getAt(i)); // updating the selection relate data
7484             }else{
7485                 s.remove(r);
7486             }
7487         });
7488     },
7489
7490     // private
7491     onRemove : function(v, index, r){
7492         this.selections.remove(r);
7493     },
7494
7495     // private
7496     onRowUpdated : function(v, index, r){
7497         if(this.isSelected(r)){
7498             v.onRowSelect(index);
7499         }
7500     },
7501
7502     /**
7503      * Select records.
7504      * @param {Array} records The records to select
7505      * @param {Boolean} keepExisting (optional) True to keep existing selections
7506      */
7507     selectRecords : function(records, keepExisting){
7508         if(!keepExisting){
7509             this.clearSelections();
7510         }
7511         var ds = this.grid.dataSource;
7512         for(var i = 0, len = records.length; i < len; i++){
7513             this.selectRow(ds.indexOf(records[i]), true);
7514         }
7515     },
7516
7517     /**
7518      * Gets the number of selected rows.
7519      * @return {Number}
7520      */
7521     getCount : function(){
7522         return this.selections.length;
7523     },
7524
7525     /**
7526      * Selects the first row in the grid.
7527      */
7528     selectFirstRow : function(){
7529         this.selectRow(0);
7530     },
7531
7532     /**
7533      * Select the last row.
7534      * @param {Boolean} keepExisting (optional) True to keep existing selections
7535      */
7536     selectLastRow : function(keepExisting){
7537         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7538     },
7539
7540     /**
7541      * Selects the row immediately following the last selected row.
7542      * @param {Boolean} keepExisting (optional) True to keep existing selections
7543      */
7544     selectNext : function(keepExisting){
7545         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
7546             this.selectRow(this.last+1, keepExisting);
7547             var view = this.grid.view ? this.grid.view : this.grid;
7548             view.focusRow(this.last);
7549         }
7550     },
7551
7552     /**
7553      * Selects the row that precedes the last selected row.
7554      * @param {Boolean} keepExisting (optional) True to keep existing selections
7555      */
7556     selectPrevious : function(keepExisting){
7557         if(this.last){
7558             this.selectRow(this.last-1, keepExisting);
7559             this.grid.getView().focusRow(this.last);
7560         }
7561     },
7562
7563     /**
7564      * Returns the selected records
7565      * @return {Array} Array of selected records
7566      */
7567     getSelections : function(){
7568         return [].concat(this.selections.items);
7569     },
7570
7571     /**
7572      * Returns the first selected record.
7573      * @return {Record}
7574      */
7575     getSelected : function(){
7576         return this.selections.itemAt(0);
7577     },
7578
7579
7580     /**
7581      * Clears all selections.
7582      */
7583     clearSelections : function(fast){
7584         if(this.locked) {
7585             return;
7586         }
7587         if(fast !== true){
7588             var ds = this.grid.dataSource;
7589             var s = this.selections;
7590             s.each(function(r){
7591                 this.deselectRow(ds.indexOfId(r.id));
7592             }, this);
7593             s.clear();
7594         }else{
7595             this.selections.clear();
7596         }
7597         this.last = false;
7598     },
7599
7600
7601     /**
7602      * Selects all rows.
7603      */
7604     selectAll : function(){
7605         if(this.locked) {
7606             return;
7607         }
7608         this.selections.clear();
7609         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
7610             this.selectRow(i, true);
7611         }
7612     },
7613
7614     /**
7615      * Returns True if there is a selection.
7616      * @return {Boolean}
7617      */
7618     hasSelection : function(){
7619         return this.selections.length > 0;
7620     },
7621
7622     /**
7623      * Returns True if the specified row is selected.
7624      * @param {Number/Record} record The record or index of the record to check
7625      * @return {Boolean}
7626      */
7627     isSelected : function(index){
7628         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
7629         return (r && this.selections.key(r.id) ? true : false);
7630     },
7631
7632     /**
7633      * Returns True if the specified record id is selected.
7634      * @param {String} id The id of record to check
7635      * @return {Boolean}
7636      */
7637     isIdSelected : function(id){
7638         return (this.selections.key(id) ? true : false);
7639     },
7640
7641     // private
7642     handleMouseDown : function(e, t)
7643     {
7644         var view = this.grid.view ? this.grid.view : this.grid;
7645         var rowIndex;
7646         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7647             return;
7648         };
7649         if(e.shiftKey && this.last !== false){
7650             var last = this.last;
7651             this.selectRange(last, rowIndex, e.ctrlKey);
7652             this.last = last; // reset the last
7653             view.focusRow(rowIndex);
7654         }else{
7655             var isSelected = this.isSelected(rowIndex);
7656             if(e.button !== 0 && isSelected){
7657                 view.focusRow(rowIndex);
7658             }else if(e.ctrlKey && isSelected){
7659                 this.deselectRow(rowIndex);
7660             }else if(!isSelected){
7661                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7662                 view.focusRow(rowIndex);
7663             }
7664         }
7665         this.fireEvent("afterselectionchange", this);
7666     },
7667     // private
7668     handleDragableRowClick :  function(grid, rowIndex, e) 
7669     {
7670         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7671             this.selectRow(rowIndex, false);
7672             var view = this.grid.view ? this.grid.view : this.grid;
7673             view.focusRow(rowIndex);
7674              this.fireEvent("afterselectionchange", this);
7675         }
7676     },
7677     
7678     /**
7679      * Selects multiple rows.
7680      * @param {Array} rows Array of the indexes of the row to select
7681      * @param {Boolean} keepExisting (optional) True to keep existing selections
7682      */
7683     selectRows : function(rows, keepExisting){
7684         if(!keepExisting){
7685             this.clearSelections();
7686         }
7687         for(var i = 0, len = rows.length; i < len; i++){
7688             this.selectRow(rows[i], true);
7689         }
7690     },
7691
7692     /**
7693      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7694      * @param {Number} startRow The index of the first row in the range
7695      * @param {Number} endRow The index of the last row in the range
7696      * @param {Boolean} keepExisting (optional) True to retain existing selections
7697      */
7698     selectRange : function(startRow, endRow, keepExisting){
7699         if(this.locked) {
7700             return;
7701         }
7702         if(!keepExisting){
7703             this.clearSelections();
7704         }
7705         if(startRow <= endRow){
7706             for(var i = startRow; i <= endRow; i++){
7707                 this.selectRow(i, true);
7708             }
7709         }else{
7710             for(var i = startRow; i >= endRow; i--){
7711                 this.selectRow(i, true);
7712             }
7713         }
7714     },
7715
7716     /**
7717      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7718      * @param {Number} startRow The index of the first row in the range
7719      * @param {Number} endRow The index of the last row in the range
7720      */
7721     deselectRange : function(startRow, endRow, preventViewNotify){
7722         if(this.locked) {
7723             return;
7724         }
7725         for(var i = startRow; i <= endRow; i++){
7726             this.deselectRow(i, preventViewNotify);
7727         }
7728     },
7729
7730     /**
7731      * Selects a row.
7732      * @param {Number} row The index of the row to select
7733      * @param {Boolean} keepExisting (optional) True to keep existing selections
7734      */
7735     selectRow : function(index, keepExisting, preventViewNotify){
7736         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
7737             return;
7738         }
7739         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7740             if(!keepExisting || this.singleSelect){
7741                 this.clearSelections();
7742             }
7743             var r = this.grid.dataSource.getAt(index);
7744             this.selections.add(r);
7745             this.last = this.lastActive = index;
7746             if(!preventViewNotify){
7747                 this.grid.getView().onRowSelect(index);
7748             }
7749             this.fireEvent("rowselect", this, index, r);
7750             this.fireEvent("selectionchange", this);
7751         }
7752     },
7753
7754     /**
7755      * Deselects a row.
7756      * @param {Number} row The index of the row to deselect
7757      */
7758     deselectRow : function(index, preventViewNotify){
7759         if(this.locked) {
7760             return;
7761         }
7762         if(this.last == index){
7763             this.last = false;
7764         }
7765         if(this.lastActive == index){
7766             this.lastActive = false;
7767         }
7768         var r = this.grid.dataSource.getAt(index);
7769         this.selections.remove(r);
7770         if(!preventViewNotify){
7771             this.grid.getView().onRowDeselect(index);
7772         }
7773         this.fireEvent("rowdeselect", this, index);
7774         this.fireEvent("selectionchange", this);
7775     },
7776
7777     // private
7778     restoreLast : function(){
7779         if(this._last){
7780             this.last = this._last;
7781         }
7782     },
7783
7784     // private
7785     acceptsNav : function(row, col, cm){
7786         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7787     },
7788
7789     // private
7790     onEditorKey : function(field, e){
7791         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7792         if(k == e.TAB){
7793             e.stopEvent();
7794             ed.completeEdit();
7795             if(e.shiftKey){
7796                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7797             }else{
7798                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7799             }
7800         }else if(k == e.ENTER && !e.ctrlKey){
7801             e.stopEvent();
7802             ed.completeEdit();
7803             if(e.shiftKey){
7804                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7805             }else{
7806                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7807             }
7808         }else if(k == e.ESC){
7809             ed.cancelEdit();
7810         }
7811         if(newCell){
7812             g.startEditing(newCell[0], newCell[1]);
7813         }
7814     }
7815 });/*
7816  * Based on:
7817  * Ext JS Library 1.1.1
7818  * Copyright(c) 2006-2007, Ext JS, LLC.
7819  *
7820  * Originally Released Under LGPL - original licence link has changed is not relivant.
7821  *
7822  * Fork - LGPL
7823  * <script type="text/javascript">
7824  */
7825  
7826
7827 /**
7828  * @class Roo.grid.ColumnModel
7829  * @extends Roo.util.Observable
7830  * This is the default implementation of a ColumnModel used by the Grid. It defines
7831  * the columns in the grid.
7832  * <br>Usage:<br>
7833  <pre><code>
7834  var colModel = new Roo.grid.ColumnModel([
7835         {header: "Ticker", width: 60, sortable: true, locked: true},
7836         {header: "Company Name", width: 150, sortable: true},
7837         {header: "Market Cap.", width: 100, sortable: true},
7838         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7839         {header: "Employees", width: 100, sortable: true, resizable: false}
7840  ]);
7841  </code></pre>
7842  * <p>
7843  
7844  * The config options listed for this class are options which may appear in each
7845  * individual column definition.
7846  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7847  * @constructor
7848  * @param {Object} config An Array of column config objects. See this class's
7849  * config objects for details.
7850 */
7851 Roo.grid.ColumnModel = function(config){
7852         /**
7853      * The config passed into the constructor
7854      */
7855     this.config = []; //config;
7856     this.lookup = {};
7857
7858     // if no id, create one
7859     // if the column does not have a dataIndex mapping,
7860     // map it to the order it is in the config
7861     for(var i = 0, len = config.length; i < len; i++){
7862         this.addColumn(config[i]);
7863         
7864     }
7865
7866     /**
7867      * The width of columns which have no width specified (defaults to 100)
7868      * @type Number
7869      */
7870     this.defaultWidth = 100;
7871
7872     /**
7873      * Default sortable of columns which have no sortable specified (defaults to false)
7874      * @type Boolean
7875      */
7876     this.defaultSortable = false;
7877
7878     this.addEvents({
7879         /**
7880              * @event widthchange
7881              * Fires when the width of a column changes.
7882              * @param {ColumnModel} this
7883              * @param {Number} columnIndex The column index
7884              * @param {Number} newWidth The new width
7885              */
7886             "widthchange": true,
7887         /**
7888              * @event headerchange
7889              * Fires when the text of a header changes.
7890              * @param {ColumnModel} this
7891              * @param {Number} columnIndex The column index
7892              * @param {Number} newText The new header text
7893              */
7894             "headerchange": true,
7895         /**
7896              * @event hiddenchange
7897              * Fires when a column is hidden or "unhidden".
7898              * @param {ColumnModel} this
7899              * @param {Number} columnIndex The column index
7900              * @param {Boolean} hidden true if hidden, false otherwise
7901              */
7902             "hiddenchange": true,
7903             /**
7904          * @event columnmoved
7905          * Fires when a column is moved.
7906          * @param {ColumnModel} this
7907          * @param {Number} oldIndex
7908          * @param {Number} newIndex
7909          */
7910         "columnmoved" : true,
7911         /**
7912          * @event columlockchange
7913          * Fires when a column's locked state is changed
7914          * @param {ColumnModel} this
7915          * @param {Number} colIndex
7916          * @param {Boolean} locked true if locked
7917          */
7918         "columnlockchange" : true
7919     });
7920     Roo.grid.ColumnModel.superclass.constructor.call(this);
7921 };
7922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7923     /**
7924      * @cfg {String} header The header text to display in the Grid view.
7925      */
7926     /**
7927      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7928      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7929      * specified, the column's index is used as an index into the Record's data Array.
7930      */
7931     /**
7932      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7933      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7934      */
7935     /**
7936      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7937      * Defaults to the value of the {@link #defaultSortable} property.
7938      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7939      */
7940     /**
7941      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7942      */
7943     /**
7944      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7945      */
7946     /**
7947      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7948      */
7949     /**
7950      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7951      */
7952     /**
7953      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7954      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7955      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7956      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7957      */
7958        /**
7959      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7960      */
7961     /**
7962      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7963      */
7964     /**
7965      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7966      */
7967     /**
7968      * @cfg {String} cursor (Optional)
7969      */
7970     /**
7971      * @cfg {String} tooltip (Optional)
7972      */
7973     /**
7974      * @cfg {Number} xs (Optional)
7975      */
7976     /**
7977      * @cfg {Number} sm (Optional)
7978      */
7979     /**
7980      * @cfg {Number} md (Optional)
7981      */
7982     /**
7983      * @cfg {Number} lg (Optional)
7984      */
7985     /**
7986      * Returns the id of the column at the specified index.
7987      * @param {Number} index The column index
7988      * @return {String} the id
7989      */
7990     getColumnId : function(index){
7991         return this.config[index].id;
7992     },
7993
7994     /**
7995      * Returns the column for a specified id.
7996      * @param {String} id The column id
7997      * @return {Object} the column
7998      */
7999     getColumnById : function(id){
8000         return this.lookup[id];
8001     },
8002
8003     
8004     /**
8005      * Returns the column Object for a specified dataIndex.
8006      * @param {String} dataIndex The column dataIndex
8007      * @return {Object|Boolean} the column or false if not found
8008      */
8009     getColumnByDataIndex: function(dataIndex){
8010         var index = this.findColumnIndex(dataIndex);
8011         return index > -1 ? this.config[index] : false;
8012     },
8013     
8014     /**
8015      * Returns the index for a specified column id.
8016      * @param {String} id The column id
8017      * @return {Number} the index, or -1 if not found
8018      */
8019     getIndexById : function(id){
8020         for(var i = 0, len = this.config.length; i < len; i++){
8021             if(this.config[i].id == id){
8022                 return i;
8023             }
8024         }
8025         return -1;
8026     },
8027     
8028     /**
8029      * Returns the index for a specified column dataIndex.
8030      * @param {String} dataIndex The column dataIndex
8031      * @return {Number} the index, or -1 if not found
8032      */
8033     
8034     findColumnIndex : function(dataIndex){
8035         for(var i = 0, len = this.config.length; i < len; i++){
8036             if(this.config[i].dataIndex == dataIndex){
8037                 return i;
8038             }
8039         }
8040         return -1;
8041     },
8042     
8043     
8044     moveColumn : function(oldIndex, newIndex){
8045         var c = this.config[oldIndex];
8046         this.config.splice(oldIndex, 1);
8047         this.config.splice(newIndex, 0, c);
8048         this.dataMap = null;
8049         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8050     },
8051
8052     isLocked : function(colIndex){
8053         return this.config[colIndex].locked === true;
8054     },
8055
8056     setLocked : function(colIndex, value, suppressEvent){
8057         if(this.isLocked(colIndex) == value){
8058             return;
8059         }
8060         this.config[colIndex].locked = value;
8061         if(!suppressEvent){
8062             this.fireEvent("columnlockchange", this, colIndex, value);
8063         }
8064     },
8065
8066     getTotalLockedWidth : function(){
8067         var totalWidth = 0;
8068         for(var i = 0; i < this.config.length; i++){
8069             if(this.isLocked(i) && !this.isHidden(i)){
8070                 this.totalWidth += this.getColumnWidth(i);
8071             }
8072         }
8073         return totalWidth;
8074     },
8075
8076     getLockedCount : function(){
8077         for(var i = 0, len = this.config.length; i < len; i++){
8078             if(!this.isLocked(i)){
8079                 return i;
8080             }
8081         }
8082         
8083         return this.config.length;
8084     },
8085
8086     /**
8087      * Returns the number of columns.
8088      * @return {Number}
8089      */
8090     getColumnCount : function(visibleOnly){
8091         if(visibleOnly === true){
8092             var c = 0;
8093             for(var i = 0, len = this.config.length; i < len; i++){
8094                 if(!this.isHidden(i)){
8095                     c++;
8096                 }
8097             }
8098             return c;
8099         }
8100         return this.config.length;
8101     },
8102
8103     /**
8104      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8105      * @param {Function} fn
8106      * @param {Object} scope (optional)
8107      * @return {Array} result
8108      */
8109     getColumnsBy : function(fn, scope){
8110         var r = [];
8111         for(var i = 0, len = this.config.length; i < len; i++){
8112             var c = this.config[i];
8113             if(fn.call(scope||this, c, i) === true){
8114                 r[r.length] = c;
8115             }
8116         }
8117         return r;
8118     },
8119
8120     /**
8121      * Returns true if the specified column is sortable.
8122      * @param {Number} col The column index
8123      * @return {Boolean}
8124      */
8125     isSortable : function(col){
8126         if(typeof this.config[col].sortable == "undefined"){
8127             return this.defaultSortable;
8128         }
8129         return this.config[col].sortable;
8130     },
8131
8132     /**
8133      * Returns the rendering (formatting) function defined for the column.
8134      * @param {Number} col The column index.
8135      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8136      */
8137     getRenderer : function(col){
8138         if(!this.config[col].renderer){
8139             return Roo.grid.ColumnModel.defaultRenderer;
8140         }
8141         return this.config[col].renderer;
8142     },
8143
8144     /**
8145      * Sets the rendering (formatting) function for a column.
8146      * @param {Number} col The column index
8147      * @param {Function} fn The function to use to process the cell's raw data
8148      * to return HTML markup for the grid view. The render function is called with
8149      * the following parameters:<ul>
8150      * <li>Data value.</li>
8151      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8152      * <li>css A CSS style string to apply to the table cell.</li>
8153      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8154      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8155      * <li>Row index</li>
8156      * <li>Column index</li>
8157      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8158      */
8159     setRenderer : function(col, fn){
8160         this.config[col].renderer = fn;
8161     },
8162
8163     /**
8164      * Returns the width for the specified column.
8165      * @param {Number} col The column index
8166      * @return {Number}
8167      */
8168     getColumnWidth : function(col){
8169         return this.config[col].width * 1 || this.defaultWidth;
8170     },
8171
8172     /**
8173      * Sets the width for a column.
8174      * @param {Number} col The column index
8175      * @param {Number} width The new width
8176      */
8177     setColumnWidth : function(col, width, suppressEvent){
8178         this.config[col].width = width;
8179         this.totalWidth = null;
8180         if(!suppressEvent){
8181              this.fireEvent("widthchange", this, col, width);
8182         }
8183     },
8184
8185     /**
8186      * Returns the total width of all columns.
8187      * @param {Boolean} includeHidden True to include hidden column widths
8188      * @return {Number}
8189      */
8190     getTotalWidth : function(includeHidden){
8191         if(!this.totalWidth){
8192             this.totalWidth = 0;
8193             for(var i = 0, len = this.config.length; i < len; i++){
8194                 if(includeHidden || !this.isHidden(i)){
8195                     this.totalWidth += this.getColumnWidth(i);
8196                 }
8197             }
8198         }
8199         return this.totalWidth;
8200     },
8201
8202     /**
8203      * Returns the header for the specified column.
8204      * @param {Number} col The column index
8205      * @return {String}
8206      */
8207     getColumnHeader : function(col){
8208         return this.config[col].header;
8209     },
8210
8211     /**
8212      * Sets the header for a column.
8213      * @param {Number} col The column index
8214      * @param {String} header The new header
8215      */
8216     setColumnHeader : function(col, header){
8217         this.config[col].header = header;
8218         this.fireEvent("headerchange", this, col, header);
8219     },
8220
8221     /**
8222      * Returns the tooltip for the specified column.
8223      * @param {Number} col The column index
8224      * @return {String}
8225      */
8226     getColumnTooltip : function(col){
8227             return this.config[col].tooltip;
8228     },
8229     /**
8230      * Sets the tooltip for a column.
8231      * @param {Number} col The column index
8232      * @param {String} tooltip The new tooltip
8233      */
8234     setColumnTooltip : function(col, tooltip){
8235             this.config[col].tooltip = tooltip;
8236     },
8237
8238     /**
8239      * Returns the dataIndex for the specified column.
8240      * @param {Number} col The column index
8241      * @return {Number}
8242      */
8243     getDataIndex : function(col){
8244         return this.config[col].dataIndex;
8245     },
8246
8247     /**
8248      * Sets the dataIndex for a column.
8249      * @param {Number} col The column index
8250      * @param {Number} dataIndex The new dataIndex
8251      */
8252     setDataIndex : function(col, dataIndex){
8253         this.config[col].dataIndex = dataIndex;
8254     },
8255
8256     
8257     
8258     /**
8259      * Returns true if the cell is editable.
8260      * @param {Number} colIndex The column index
8261      * @param {Number} rowIndex The row index - this is nto actually used..?
8262      * @return {Boolean}
8263      */
8264     isCellEditable : function(colIndex, rowIndex){
8265         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8266     },
8267
8268     /**
8269      * Returns the editor defined for the cell/column.
8270      * return false or null to disable editing.
8271      * @param {Number} colIndex The column index
8272      * @param {Number} rowIndex The row index
8273      * @return {Object}
8274      */
8275     getCellEditor : function(colIndex, rowIndex){
8276         return this.config[colIndex].editor;
8277     },
8278
8279     /**
8280      * Sets if a column is editable.
8281      * @param {Number} col The column index
8282      * @param {Boolean} editable True if the column is editable
8283      */
8284     setEditable : function(col, editable){
8285         this.config[col].editable = editable;
8286     },
8287
8288
8289     /**
8290      * Returns true if the column is hidden.
8291      * @param {Number} colIndex The column index
8292      * @return {Boolean}
8293      */
8294     isHidden : function(colIndex){
8295         return this.config[colIndex].hidden;
8296     },
8297
8298
8299     /**
8300      * Returns true if the column width cannot be changed
8301      */
8302     isFixed : function(colIndex){
8303         return this.config[colIndex].fixed;
8304     },
8305
8306     /**
8307      * Returns true if the column can be resized
8308      * @return {Boolean}
8309      */
8310     isResizable : function(colIndex){
8311         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8312     },
8313     /**
8314      * Sets if a column is hidden.
8315      * @param {Number} colIndex The column index
8316      * @param {Boolean} hidden True if the column is hidden
8317      */
8318     setHidden : function(colIndex, hidden){
8319         this.config[colIndex].hidden = hidden;
8320         this.totalWidth = null;
8321         this.fireEvent("hiddenchange", this, colIndex, hidden);
8322     },
8323
8324     /**
8325      * Sets the editor for a column.
8326      * @param {Number} col The column index
8327      * @param {Object} editor The editor object
8328      */
8329     setEditor : function(col, editor){
8330         this.config[col].editor = editor;
8331     },
8332     /**
8333      * Add a column (experimental...) - defaults to adding to the end..
8334      * @param {Object} config 
8335     */
8336     addColumn : function(c)
8337     {
8338     
8339         var i = this.config.length;
8340         this.config[i] = c;
8341         
8342         if(typeof c.dataIndex == "undefined"){
8343             c.dataIndex = i;
8344         }
8345         if(typeof c.renderer == "string"){
8346             c.renderer = Roo.util.Format[c.renderer];
8347         }
8348         if(typeof c.id == "undefined"){
8349             c.id = Roo.id();
8350         }
8351         if(c.editor && c.editor.xtype){
8352             c.editor  = Roo.factory(c.editor, Roo.grid);
8353         }
8354         if(c.editor && c.editor.isFormField){
8355             c.editor = new Roo.grid.GridEditor(c.editor);
8356         }
8357         this.lookup[c.id] = c;
8358     }
8359     
8360 });
8361
8362 Roo.grid.ColumnModel.defaultRenderer = function(value)
8363 {
8364     if(typeof value == "object") {
8365         return value;
8366     }
8367         if(typeof value == "string" && value.length < 1){
8368             return "&#160;";
8369         }
8370     
8371         return String.format("{0}", value);
8372 };
8373
8374 // Alias for backwards compatibility
8375 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8376 /*
8377  * Based on:
8378  * Ext JS Library 1.1.1
8379  * Copyright(c) 2006-2007, Ext JS, LLC.
8380  *
8381  * Originally Released Under LGPL - original licence link has changed is not relivant.
8382  *
8383  * Fork - LGPL
8384  * <script type="text/javascript">
8385  */
8386  
8387 /**
8388  * @class Roo.LoadMask
8389  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8390  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8391  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8392  * element's UpdateManager load indicator and will be destroyed after the initial load.
8393  * @constructor
8394  * Create a new LoadMask
8395  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8396  * @param {Object} config The config object
8397  */
8398 Roo.LoadMask = function(el, config){
8399     this.el = Roo.get(el);
8400     Roo.apply(this, config);
8401     if(this.store){
8402         this.store.on('beforeload', this.onBeforeLoad, this);
8403         this.store.on('load', this.onLoad, this);
8404         this.store.on('loadexception', this.onLoadException, this);
8405         this.removeMask = false;
8406     }else{
8407         var um = this.el.getUpdateManager();
8408         um.showLoadIndicator = false; // disable the default indicator
8409         um.on('beforeupdate', this.onBeforeLoad, this);
8410         um.on('update', this.onLoad, this);
8411         um.on('failure', this.onLoad, this);
8412         this.removeMask = true;
8413     }
8414 };
8415
8416 Roo.LoadMask.prototype = {
8417     /**
8418      * @cfg {Boolean} removeMask
8419      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8420      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8421      */
8422     /**
8423      * @cfg {String} msg
8424      * The text to display in a centered loading message box (defaults to 'Loading...')
8425      */
8426     msg : 'Loading...',
8427     /**
8428      * @cfg {String} msgCls
8429      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8430      */
8431     msgCls : 'x-mask-loading',
8432
8433     /**
8434      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8435      * @type Boolean
8436      */
8437     disabled: false,
8438
8439     /**
8440      * Disables the mask to prevent it from being displayed
8441      */
8442     disable : function(){
8443        this.disabled = true;
8444     },
8445
8446     /**
8447      * Enables the mask so that it can be displayed
8448      */
8449     enable : function(){
8450         this.disabled = false;
8451     },
8452     
8453     onLoadException : function()
8454     {
8455         Roo.log(arguments);
8456         
8457         if (typeof(arguments[3]) != 'undefined') {
8458             Roo.MessageBox.alert("Error loading",arguments[3]);
8459         } 
8460         /*
8461         try {
8462             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8463                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8464             }   
8465         } catch(e) {
8466             
8467         }
8468         */
8469     
8470         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8471     },
8472     // private
8473     onLoad : function()
8474     {
8475         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8476     },
8477
8478     // private
8479     onBeforeLoad : function(){
8480         if(!this.disabled){
8481             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8482         }
8483     },
8484
8485     // private
8486     destroy : function(){
8487         if(this.store){
8488             this.store.un('beforeload', this.onBeforeLoad, this);
8489             this.store.un('load', this.onLoad, this);
8490             this.store.un('loadexception', this.onLoadException, this);
8491         }else{
8492             var um = this.el.getUpdateManager();
8493             um.un('beforeupdate', this.onBeforeLoad, this);
8494             um.un('update', this.onLoad, this);
8495             um.un('failure', this.onLoad, this);
8496         }
8497     }
8498 };/**
8499  * @class Roo.bootstrap.Table
8500  * @licence LGBL
8501  * @extends Roo.bootstrap.Component
8502  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8503  * Similar to Roo.grid.Grid
8504  * <pre><code>
8505  var table = Roo.factory({
8506     xtype : 'Table',
8507     xns : Roo.bootstrap,
8508     autoSizeColumns: true,
8509     
8510     
8511     store : {
8512         xtype : 'Store',
8513         xns : Roo.data,
8514         remoteSort : true,
8515         sortInfo : { direction : 'ASC', field: 'name' },
8516         proxy : {
8517            xtype : 'HttpProxy',
8518            xns : Roo.data,
8519            method : 'GET',
8520            url : 'https://example.com/some.data.url.json'
8521         },
8522         reader : {
8523            xtype : 'JsonReader',
8524            xns : Roo.data,
8525            fields : [ 'id', 'name', whatever' ],
8526            id : 'id',
8527            root : 'data'
8528         }
8529     },
8530     cm : [
8531         {
8532             xtype : 'ColumnModel',
8533             xns : Roo.grid,
8534             align : 'center',
8535             cursor : 'pointer',
8536             dataIndex : 'is_in_group',
8537             header : "Name",
8538             sortable : true,
8539             renderer : function(v, x , r) {  
8540             
8541                 return String.format("{0}", v)
8542             }
8543             width : 3
8544         } // more columns..
8545     ],
8546     selModel : {
8547         xtype : 'RowSelectionModel',
8548         xns : Roo.bootstrap.Table
8549         // you can add listeners to catch selection change here....
8550     }
8551      
8552
8553  });
8554  // set any options
8555  grid.render(Roo.get("some-div"));
8556 </code></pre>
8557
8558 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8559
8560
8561
8562  *
8563  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8564  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8565  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8566  * 
8567  * @cfg {String} cls table class
8568  *
8569  * 
8570  * @cfg {boolean} striped Should the rows be alternative striped
8571  * @cfg {boolean} bordered Add borders to the table
8572  * @cfg {boolean} hover Add hover highlighting
8573  * @cfg {boolean} condensed Format condensed
8574  * @cfg {boolean} responsive Format condensed
8575  * @cfg {Boolean} loadMask (true|false) default false
8576  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8577  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8578  * @cfg {Boolean} rowSelection (true|false) default false
8579  * @cfg {Boolean} cellSelection (true|false) default false
8580  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8581  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8582  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8583  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8584  
8585  * 
8586  * @constructor
8587  * Create a new Table
8588  * @param {Object} config The config object
8589  */
8590
8591 Roo.bootstrap.Table = function(config)
8592 {
8593     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8594      
8595     // BC...
8596     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8597     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8598     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8599     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8600     
8601     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8602     if (this.sm) {
8603         this.sm.grid = this;
8604         this.selModel = Roo.factory(this.sm, Roo.grid);
8605         this.sm = this.selModel;
8606         this.sm.xmodule = this.xmodule || false;
8607     }
8608     
8609     if (this.cm && typeof(this.cm.config) == 'undefined') {
8610         this.colModel = new Roo.grid.ColumnModel(this.cm);
8611         this.cm = this.colModel;
8612         this.cm.xmodule = this.xmodule || false;
8613     }
8614     if (this.store) {
8615         this.store= Roo.factory(this.store, Roo.data);
8616         this.ds = this.store;
8617         this.ds.xmodule = this.xmodule || false;
8618          
8619     }
8620     if (this.footer && this.store) {
8621         this.footer.dataSource = this.ds;
8622         this.footer = Roo.factory(this.footer);
8623     }
8624     
8625     /** @private */
8626     this.addEvents({
8627         /**
8628          * @event cellclick
8629          * Fires when a cell is clicked
8630          * @param {Roo.bootstrap.Table} this
8631          * @param {Roo.Element} el
8632          * @param {Number} rowIndex
8633          * @param {Number} columnIndex
8634          * @param {Roo.EventObject} e
8635          */
8636         "cellclick" : true,
8637         /**
8638          * @event celldblclick
8639          * Fires when a cell is double clicked
8640          * @param {Roo.bootstrap.Table} this
8641          * @param {Roo.Element} el
8642          * @param {Number} rowIndex
8643          * @param {Number} columnIndex
8644          * @param {Roo.EventObject} e
8645          */
8646         "celldblclick" : true,
8647         /**
8648          * @event rowclick
8649          * Fires when a row is clicked
8650          * @param {Roo.bootstrap.Table} this
8651          * @param {Roo.Element} el
8652          * @param {Number} rowIndex
8653          * @param {Roo.EventObject} e
8654          */
8655         "rowclick" : true,
8656         /**
8657          * @event rowdblclick
8658          * Fires when a row is double clicked
8659          * @param {Roo.bootstrap.Table} this
8660          * @param {Roo.Element} el
8661          * @param {Number} rowIndex
8662          * @param {Roo.EventObject} e
8663          */
8664         "rowdblclick" : true,
8665         /**
8666          * @event mouseover
8667          * Fires when a mouseover occur
8668          * @param {Roo.bootstrap.Table} this
8669          * @param {Roo.Element} el
8670          * @param {Number} rowIndex
8671          * @param {Number} columnIndex
8672          * @param {Roo.EventObject} e
8673          */
8674         "mouseover" : true,
8675         /**
8676          * @event mouseout
8677          * Fires when a mouseout occur
8678          * @param {Roo.bootstrap.Table} this
8679          * @param {Roo.Element} el
8680          * @param {Number} rowIndex
8681          * @param {Number} columnIndex
8682          * @param {Roo.EventObject} e
8683          */
8684         "mouseout" : true,
8685         /**
8686          * @event rowclass
8687          * Fires when a row is rendered, so you can change add a style to it.
8688          * @param {Roo.bootstrap.Table} this
8689          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8690          */
8691         'rowclass' : true,
8692           /**
8693          * @event rowsrendered
8694          * Fires when all the  rows have been rendered
8695          * @param {Roo.bootstrap.Table} this
8696          */
8697         'rowsrendered' : true,
8698         /**
8699          * @event contextmenu
8700          * The raw contextmenu event for the entire grid.
8701          * @param {Roo.EventObject} e
8702          */
8703         "contextmenu" : true,
8704         /**
8705          * @event rowcontextmenu
8706          * Fires when a row is right clicked
8707          * @param {Roo.bootstrap.Table} this
8708          * @param {Number} rowIndex
8709          * @param {Roo.EventObject} e
8710          */
8711         "rowcontextmenu" : true,
8712         /**
8713          * @event cellcontextmenu
8714          * Fires when a cell is right clicked
8715          * @param {Roo.bootstrap.Table} this
8716          * @param {Number} rowIndex
8717          * @param {Number} cellIndex
8718          * @param {Roo.EventObject} e
8719          */
8720          "cellcontextmenu" : true,
8721          /**
8722          * @event headercontextmenu
8723          * Fires when a header is right clicked
8724          * @param {Roo.bootstrap.Table} this
8725          * @param {Number} columnIndex
8726          * @param {Roo.EventObject} e
8727          */
8728         "headercontextmenu" : true,
8729         /**
8730          * @event mousedown
8731          * The raw mousedown event for the entire grid.
8732          * @param {Roo.EventObject} e
8733          */
8734         "mousedown" : true
8735         
8736     });
8737 };
8738
8739 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8740     
8741     cls: false,
8742     
8743     striped : false,
8744     scrollBody : false,
8745     bordered: false,
8746     hover:  false,
8747     condensed : false,
8748     responsive : false,
8749     sm : false,
8750     cm : false,
8751     store : false,
8752     loadMask : false,
8753     footerShow : true,
8754     headerShow : true,
8755   
8756     rowSelection : false,
8757     cellSelection : false,
8758     layout : false,
8759     
8760     // Roo.Element - the tbody
8761     mainBody: false,
8762     // Roo.Element - thead element
8763     mainHead: false,
8764     
8765     container: false, // used by gridpanel...
8766     
8767     lazyLoad : false,
8768     
8769     CSS : Roo.util.CSS,
8770     
8771     auto_hide_footer : false,
8772     
8773     getAutoCreate : function()
8774     {
8775         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8776         
8777         cfg = {
8778             tag: 'table',
8779             cls : 'table',
8780             cn : []
8781         };
8782         // this get's auto added by panel.Grid
8783         if (this.scrollBody) {
8784             cfg.cls += ' table-body-fixed';
8785         }    
8786         if (this.striped) {
8787             cfg.cls += ' table-striped';
8788         }
8789         
8790         if (this.hover) {
8791             cfg.cls += ' table-hover';
8792         }
8793         if (this.bordered) {
8794             cfg.cls += ' table-bordered';
8795         }
8796         if (this.condensed) {
8797             cfg.cls += ' table-condensed';
8798         }
8799         
8800         if (this.responsive) {
8801             cfg.cls += ' table-responsive';
8802         }
8803         
8804         if (this.cls) {
8805             cfg.cls+=  ' ' +this.cls;
8806         }
8807         
8808         
8809         
8810         if (this.layout) {
8811             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8812         }
8813         
8814         if(this.store || this.cm){
8815             if(this.headerShow){
8816                 cfg.cn.push(this.renderHeader());
8817             }
8818             
8819             cfg.cn.push(this.renderBody());
8820             
8821             if(this.footerShow){
8822                 cfg.cn.push(this.renderFooter());
8823             }
8824             // where does this come from?
8825             //cfg.cls+=  ' TableGrid';
8826         }
8827         
8828         return { cn : [ cfg ] };
8829     },
8830     
8831     initEvents : function()
8832     {   
8833         if(!this.store || !this.cm){
8834             return;
8835         }
8836         if (this.selModel) {
8837             this.selModel.initEvents();
8838         }
8839         
8840         
8841         //Roo.log('initEvents with ds!!!!');
8842         
8843         this.mainBody = this.el.select('tbody', true).first();
8844         this.mainHead = this.el.select('thead', true).first();
8845         this.mainFoot = this.el.select('tfoot', true).first();
8846         
8847         
8848         
8849         
8850         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8851             e.on('click', this.sort, this);
8852         }, this);
8853         
8854         
8855         // why is this done????? = it breaks dialogs??
8856         //this.parent().el.setStyle('position', 'relative');
8857         
8858         
8859         if (this.footer) {
8860             this.footer.parentId = this.id;
8861             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8862             
8863             if(this.lazyLoad){
8864                 this.el.select('tfoot tr td').first().addClass('hide');
8865             }
8866         } 
8867         
8868         if(this.loadMask) {
8869             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8870         }
8871         
8872         this.store.on('load', this.onLoad, this);
8873         this.store.on('beforeload', this.onBeforeLoad, this);
8874         this.store.on('update', this.onUpdate, this);
8875         this.store.on('add', this.onAdd, this);
8876         this.store.on("clear", this.clear, this);
8877         
8878         this.el.on("contextmenu", this.onContextMenu, this);
8879         
8880         
8881         this.cm.on("headerchange", this.onHeaderChange, this);
8882         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8883
8884         
8885         this.mainBody.on("click", this.onClick, this);
8886         this.mainBody.on("dblclick", this.onDblClick, this);        
8887         this.mainBody.on('scroll', this.onBodyScroll, this);
8888
8889         // guessing mainbody will work - this relays usually caught by selmodel at present.
8890         this.relayEvents(this.mainBody, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8891   
8892         
8893         
8894         
8895     },
8896     
8897     onContextMenu : function(e, t)
8898     {
8899         this.processEvent("contextmenu", e);
8900     },
8901     
8902     processEvent : function(name, e)
8903     {
8904         if (name != 'touchstart' ) {
8905             this.fireEvent(name, e);    
8906         }
8907         
8908         var t = e.getTarget();
8909         
8910         var cell = Roo.get(t);
8911         
8912         if(!cell){
8913             return;
8914         }
8915         
8916         if(cell.findParent('tfoot', false, true)){
8917             return;
8918         }
8919         
8920         if(cell.findParent('thead', false, true)){
8921             
8922             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8923                 cell = Roo.get(t).findParent('th', false, true);
8924                 if (!cell) {
8925                     Roo.log("failed to find th in thead?");
8926                     Roo.log(e.getTarget());
8927                     return;
8928                 }
8929             }
8930             
8931             var cellIndex = cell.dom.cellIndex;
8932             
8933             var ename = name == 'touchstart' ? 'click' : name;
8934             this.fireEvent("header" + ename, this, cellIndex, e);
8935             
8936             return;
8937         }
8938         
8939         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8940             cell = Roo.get(t).findParent('td', false, true);
8941             if (!cell) {
8942                 Roo.log("failed to find th in tbody?");
8943                 Roo.log(e.getTarget());
8944                 return;
8945             }
8946         }
8947         
8948         var row = cell.findParent('tr', false, true);
8949         var cellIndex = cell.dom.cellIndex;
8950         var rowIndex = row.dom.rowIndex - 1;
8951         
8952         if(row !== false){
8953             
8954             this.fireEvent("row" + name, this, rowIndex, e);
8955             
8956             if(cell !== false){
8957             
8958                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8959             }
8960         }
8961         
8962     },
8963     
8964     onMouseover : function(e, el)
8965     {
8966         var cell = Roo.get(el);
8967         
8968         if(!cell){
8969             return;
8970         }
8971         
8972         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8973             cell = cell.findParent('td', false, true);
8974         }
8975         
8976         var row = cell.findParent('tr', false, true);
8977         var cellIndex = cell.dom.cellIndex;
8978         var rowIndex = row.dom.rowIndex - 1; // start from 0
8979         
8980         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8981         
8982     },
8983     
8984     onMouseout : function(e, el)
8985     {
8986         var cell = Roo.get(el);
8987         
8988         if(!cell){
8989             return;
8990         }
8991         
8992         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8993             cell = cell.findParent('td', false, true);
8994         }
8995         
8996         var row = cell.findParent('tr', false, true);
8997         var cellIndex = cell.dom.cellIndex;
8998         var rowIndex = row.dom.rowIndex - 1; // start from 0
8999         
9000         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9001         
9002     },
9003     
9004     onClick : function(e, el)
9005     {
9006         var cell = Roo.get(el);
9007         
9008         if(!cell || (!this.cellSelection && !this.rowSelection)){
9009             return;
9010         }
9011         
9012         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9013             cell = cell.findParent('td', false, true);
9014         }
9015         
9016         if(!cell || typeof(cell) == 'undefined'){
9017             return;
9018         }
9019         
9020         var row = cell.findParent('tr', false, true);
9021         
9022         if(!row || typeof(row) == 'undefined'){
9023             return;
9024         }
9025         
9026         var cellIndex = cell.dom.cellIndex;
9027         var rowIndex = this.getRowIndex(row);
9028         
9029         // why??? - should these not be based on SelectionModel?
9030         //if(this.cellSelection){
9031             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9032         //}
9033         
9034         //if(this.rowSelection){
9035             this.fireEvent('rowclick', this, row, rowIndex, e);
9036         //}
9037          
9038     },
9039         
9040     onDblClick : function(e,el)
9041     {
9042         var cell = Roo.get(el);
9043         
9044         if(!cell || (!this.cellSelection && !this.rowSelection)){
9045             return;
9046         }
9047         
9048         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9049             cell = cell.findParent('td', false, true);
9050         }
9051         
9052         if(!cell || typeof(cell) == 'undefined'){
9053             return;
9054         }
9055         
9056         var row = cell.findParent('tr', false, true);
9057         
9058         if(!row || typeof(row) == 'undefined'){
9059             return;
9060         }
9061         
9062         var cellIndex = cell.dom.cellIndex;
9063         var rowIndex = this.getRowIndex(row);
9064         
9065         if(this.cellSelection){
9066             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9067         }
9068         
9069         if(this.rowSelection){
9070             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9071         }
9072     },
9073     findRowIndex : function(el)
9074     {
9075         var cell = Roo.get(el);
9076         if(!cell) {
9077             return false;
9078         }
9079         var row = cell.findParent('tr', false, true);
9080         
9081         if(!row || typeof(row) == 'undefined'){
9082             return false;
9083         }
9084         return this.getRowIndex(row);
9085     },
9086     sort : function(e,el)
9087     {
9088         var col = Roo.get(el);
9089         
9090         if(!col.hasClass('sortable')){
9091             return;
9092         }
9093         
9094         var sort = col.attr('sort');
9095         var dir = 'ASC';
9096         
9097         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9098             dir = 'DESC';
9099         }
9100         
9101         this.store.sortInfo = {field : sort, direction : dir};
9102         
9103         if (this.footer) {
9104             Roo.log("calling footer first");
9105             this.footer.onClick('first');
9106         } else {
9107         
9108             this.store.load({ params : { start : 0 } });
9109         }
9110     },
9111     
9112     renderHeader : function()
9113     {
9114         var header = {
9115             tag: 'thead',
9116             cn : []
9117         };
9118         
9119         var cm = this.cm;
9120         this.totalWidth = 0;
9121         
9122         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9123             
9124             var config = cm.config[i];
9125             
9126             var c = {
9127                 tag: 'th',
9128                 cls : 'x-hcol-' + i,
9129                 style : '',
9130                 
9131                 html: cm.getColumnHeader(i)
9132             };
9133             
9134             var tooltip = cm.getColumnTooltip(i);
9135             if (tooltip) {
9136                 c.tooltip = tooltip;
9137             }
9138             
9139             
9140             var hh = '';
9141             
9142             if(typeof(config.sortable) != 'undefined' && config.sortable){
9143                 c.cls = 'sortable';
9144                 c.html = '<i class="fa"></i>' + c.html;
9145             }
9146             
9147             // could use BS4 hidden-..-down 
9148             
9149             if(typeof(config.lgHeader) != 'undefined'){
9150                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9151             }
9152             
9153             if(typeof(config.mdHeader) != 'undefined'){
9154                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9155             }
9156             
9157             if(typeof(config.smHeader) != 'undefined'){
9158                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9159             }
9160             
9161             if(typeof(config.xsHeader) != 'undefined'){
9162                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9163             }
9164             
9165             if(hh.length){
9166                 c.html = hh;
9167             }
9168             
9169             if(typeof(config.tooltip) != 'undefined'){
9170                 c.tooltip = config.tooltip;
9171             }
9172             
9173             if(typeof(config.colspan) != 'undefined'){
9174                 c.colspan = config.colspan;
9175             }
9176             
9177             if(typeof(config.hidden) != 'undefined' && config.hidden){
9178                 c.style += ' display:none;';
9179             }
9180             
9181             if(typeof(config.dataIndex) != 'undefined'){
9182                 c.sort = config.dataIndex;
9183             }
9184             
9185            
9186             
9187             if(typeof(config.align) != 'undefined' && config.align.length){
9188                 c.style += ' text-align:' + config.align + ';';
9189             }
9190             
9191             if(typeof(config.width) != 'undefined'){
9192                 c.style += ' width:' + config.width + 'px;';
9193                 this.totalWidth += config.width;
9194             } else {
9195                 this.totalWidth += 100; // assume minimum of 100 per column?
9196             }
9197             
9198             if(typeof(config.cls) != 'undefined'){
9199                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9200             }
9201             
9202             ['xs','sm','md','lg'].map(function(size){
9203                 
9204                 if(typeof(config[size]) == 'undefined'){
9205                     return;
9206                 }
9207                  
9208                 if (!config[size]) { // 0 = hidden
9209                     // BS 4 '0' is treated as hide that column and below.
9210                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9211                     return;
9212                 }
9213                 
9214                 c.cls += ' col-' + size + '-' + config[size] + (
9215                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9216                 );
9217                 
9218                 
9219             });
9220             
9221             header.cn.push(c)
9222         }
9223         
9224         return header;
9225     },
9226     
9227     renderBody : function()
9228     {
9229         var body = {
9230             tag: 'tbody',
9231             cn : [
9232                 {
9233                     tag: 'tr',
9234                     cn : [
9235                         {
9236                             tag : 'td',
9237                             colspan :  this.cm.getColumnCount()
9238                         }
9239                     ]
9240                 }
9241             ]
9242         };
9243         
9244         return body;
9245     },
9246     
9247     renderFooter : function()
9248     {
9249         var footer = {
9250             tag: 'tfoot',
9251             cn : [
9252                 {
9253                     tag: 'tr',
9254                     cn : [
9255                         {
9256                             tag : 'td',
9257                             colspan :  this.cm.getColumnCount()
9258                         }
9259                     ]
9260                 }
9261             ]
9262         };
9263         
9264         return footer;
9265     },
9266     
9267     
9268     
9269     onLoad : function()
9270     {
9271 //        Roo.log('ds onload');
9272         this.clear();
9273         
9274         var _this = this;
9275         var cm = this.cm;
9276         var ds = this.store;
9277         
9278         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9279             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9280             if (_this.store.sortInfo) {
9281                     
9282                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9283                     e.select('i', true).addClass(['fa-arrow-up']);
9284                 }
9285                 
9286                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9287                     e.select('i', true).addClass(['fa-arrow-down']);
9288                 }
9289             }
9290         });
9291         
9292         var tbody =  this.mainBody;
9293               
9294         if(ds.getCount() > 0){
9295             ds.data.each(function(d,rowIndex){
9296                 var row =  this.renderRow(cm, ds, rowIndex);
9297                 
9298                 tbody.createChild(row);
9299                 
9300                 var _this = this;
9301                 
9302                 if(row.cellObjects.length){
9303                     Roo.each(row.cellObjects, function(r){
9304                         _this.renderCellObject(r);
9305                     })
9306                 }
9307                 
9308             }, this);
9309         }
9310         
9311         var tfoot = this.el.select('tfoot', true).first();
9312         
9313         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9314             
9315             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9316             
9317             var total = this.ds.getTotalCount();
9318             
9319             if(this.footer.pageSize < total){
9320                 this.mainFoot.show();
9321             }
9322         }
9323         
9324         Roo.each(this.el.select('tbody td', true).elements, function(e){
9325             e.on('mouseover', _this.onMouseover, _this);
9326         });
9327         
9328         Roo.each(this.el.select('tbody td', true).elements, function(e){
9329             e.on('mouseout', _this.onMouseout, _this);
9330         });
9331         this.fireEvent('rowsrendered', this);
9332         
9333         this.autoSize();
9334     },
9335     
9336     
9337     onUpdate : function(ds,record)
9338     {
9339         this.refreshRow(record);
9340         this.autoSize();
9341     },
9342     
9343     onRemove : function(ds, record, index, isUpdate){
9344         if(isUpdate !== true){
9345             this.fireEvent("beforerowremoved", this, index, record);
9346         }
9347         var bt = this.mainBody.dom;
9348         
9349         var rows = this.el.select('tbody > tr', true).elements;
9350         
9351         if(typeof(rows[index]) != 'undefined'){
9352             bt.removeChild(rows[index].dom);
9353         }
9354         
9355 //        if(bt.rows[index]){
9356 //            bt.removeChild(bt.rows[index]);
9357 //        }
9358         
9359         if(isUpdate !== true){
9360             //this.stripeRows(index);
9361             //this.syncRowHeights(index, index);
9362             //this.layout();
9363             this.fireEvent("rowremoved", this, index, record);
9364         }
9365     },
9366     
9367     onAdd : function(ds, records, rowIndex)
9368     {
9369         //Roo.log('on Add called');
9370         // - note this does not handle multiple adding very well..
9371         var bt = this.mainBody.dom;
9372         for (var i =0 ; i < records.length;i++) {
9373             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9374             //Roo.log(records[i]);
9375             //Roo.log(this.store.getAt(rowIndex+i));
9376             this.insertRow(this.store, rowIndex + i, false);
9377             return;
9378         }
9379         
9380     },
9381     
9382     
9383     refreshRow : function(record){
9384         var ds = this.store, index;
9385         if(typeof record == 'number'){
9386             index = record;
9387             record = ds.getAt(index);
9388         }else{
9389             index = ds.indexOf(record);
9390             if (index < 0) {
9391                 return; // should not happen - but seems to 
9392             }
9393         }
9394         this.insertRow(ds, index, true);
9395         this.autoSize();
9396         this.onRemove(ds, record, index+1, true);
9397         this.autoSize();
9398         //this.syncRowHeights(index, index);
9399         //this.layout();
9400         this.fireEvent("rowupdated", this, index, record);
9401     },
9402     
9403     insertRow : function(dm, rowIndex, isUpdate){
9404         
9405         if(!isUpdate){
9406             this.fireEvent("beforerowsinserted", this, rowIndex);
9407         }
9408             //var s = this.getScrollState();
9409         var row = this.renderRow(this.cm, this.store, rowIndex);
9410         // insert before rowIndex..
9411         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
9412         
9413         var _this = this;
9414                 
9415         if(row.cellObjects.length){
9416             Roo.each(row.cellObjects, function(r){
9417                 _this.renderCellObject(r);
9418             })
9419         }
9420             
9421         if(!isUpdate){
9422             this.fireEvent("rowsinserted", this, rowIndex);
9423             //this.syncRowHeights(firstRow, lastRow);
9424             //this.stripeRows(firstRow);
9425             //this.layout();
9426         }
9427         
9428     },
9429     
9430     
9431     getRowDom : function(rowIndex)
9432     {
9433         var rows = this.el.select('tbody > tr', true).elements;
9434         
9435         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9436         
9437     },
9438     // returns the object tree for a tr..
9439   
9440     
9441     renderRow : function(cm, ds, rowIndex) 
9442     {
9443         var d = ds.getAt(rowIndex);
9444         
9445         var row = {
9446             tag : 'tr',
9447             cls : 'x-row-' + rowIndex,
9448             cn : []
9449         };
9450             
9451         var cellObjects = [];
9452         
9453         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9454             var config = cm.config[i];
9455             
9456             var renderer = cm.getRenderer(i);
9457             var value = '';
9458             var id = false;
9459             
9460             if(typeof(renderer) !== 'undefined'){
9461                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9462             }
9463             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9464             // and are rendered into the cells after the row is rendered - using the id for the element.
9465             
9466             if(typeof(value) === 'object'){
9467                 id = Roo.id();
9468                 cellObjects.push({
9469                     container : id,
9470                     cfg : value 
9471                 })
9472             }
9473             
9474             var rowcfg = {
9475                 record: d,
9476                 rowIndex : rowIndex,
9477                 colIndex : i,
9478                 rowClass : ''
9479             };
9480
9481             this.fireEvent('rowclass', this, rowcfg);
9482             
9483             var td = {
9484                 tag: 'td',
9485                 // this might end up displaying HTML?
9486                 // this is too messy... - better to only do it on columsn you know are going to be too long
9487                 //tooltip : (typeof(value) === 'object') ? '' : value,
9488                 cls : rowcfg.rowClass + ' x-col-' + i,
9489                 style: '',
9490                 html: (typeof(value) === 'object') ? '' : value
9491             };
9492             
9493             if (id) {
9494                 td.id = id;
9495             }
9496             
9497             if(typeof(config.colspan) != 'undefined'){
9498                 td.colspan = config.colspan;
9499             }
9500             
9501             if(typeof(config.hidden) != 'undefined' && config.hidden){
9502                 td.style += ' display:none;';
9503             }
9504             
9505             if(typeof(config.align) != 'undefined' && config.align.length){
9506                 td.style += ' text-align:' + config.align + ';';
9507             }
9508             if(typeof(config.valign) != 'undefined' && config.valign.length){
9509                 td.style += ' vertical-align:' + config.valign + ';';
9510             }
9511             
9512             if(typeof(config.width) != 'undefined'){
9513                 td.style += ' width:' +  config.width + 'px;';
9514             }
9515             
9516             if(typeof(config.cursor) != 'undefined'){
9517                 td.style += ' cursor:' +  config.cursor + ';';
9518             }
9519             
9520             if(typeof(config.cls) != 'undefined'){
9521                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9522             }
9523             
9524             ['xs','sm','md','lg'].map(function(size){
9525                 
9526                 if(typeof(config[size]) == 'undefined'){
9527                     return;
9528                 }
9529                 
9530                 
9531                   
9532                 if (!config[size]) { // 0 = hidden
9533                     // BS 4 '0' is treated as hide that column and below.
9534                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9535                     return;
9536                 }
9537                 
9538                 td.cls += ' col-' + size + '-' + config[size] + (
9539                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9540                 );
9541                  
9542
9543             });
9544             
9545             row.cn.push(td);
9546            
9547         }
9548         
9549         row.cellObjects = cellObjects;
9550         
9551         return row;
9552           
9553     },
9554     
9555     
9556     
9557     onBeforeLoad : function()
9558     {
9559         
9560     },
9561      /**
9562      * Remove all rows
9563      */
9564     clear : function()
9565     {
9566         this.el.select('tbody', true).first().dom.innerHTML = '';
9567     },
9568     /**
9569      * Show or hide a row.
9570      * @param {Number} rowIndex to show or hide
9571      * @param {Boolean} state hide
9572      */
9573     setRowVisibility : function(rowIndex, state)
9574     {
9575         var bt = this.mainBody.dom;
9576         
9577         var rows = this.el.select('tbody > tr', true).elements;
9578         
9579         if(typeof(rows[rowIndex]) == 'undefined'){
9580             return;
9581         }
9582         rows[rowIndex].dom.style.display = state ? '' : 'none';
9583     },
9584     
9585     
9586     getSelectionModel : function(){
9587         if(!this.selModel){
9588             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9589         }
9590         return this.selModel;
9591     },
9592     /*
9593      * Render the Roo.bootstrap object from renderder
9594      */
9595     renderCellObject : function(r)
9596     {
9597         var _this = this;
9598         
9599         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9600         
9601         var t = r.cfg.render(r.container);
9602         
9603         if(r.cfg.cn){
9604             Roo.each(r.cfg.cn, function(c){
9605                 var child = {
9606                     container: t.getChildContainer(),
9607                     cfg: c
9608                 };
9609                 _this.renderCellObject(child);
9610             })
9611         }
9612     },
9613     
9614     getRowIndex : function(row)
9615     {
9616         var rowIndex = -1;
9617         
9618         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9619             if(el != row){
9620                 return;
9621             }
9622             
9623             rowIndex = index;
9624         });
9625         
9626         return rowIndex;
9627     },
9628      /**
9629      * Returns the grid's underlying element = used by panel.Grid
9630      * @return {Element} The element
9631      */
9632     getGridEl : function(){
9633         return this.el;
9634     },
9635      /**
9636      * Forces a resize - used by panel.Grid
9637      * @return {Element} The element
9638      */
9639     autoSize : function()
9640     {
9641         //var ctr = Roo.get(this.container.dom.parentElement);
9642         var ctr = Roo.get(this.el.dom);
9643         
9644         var thd = this.getGridEl().select('thead',true).first();
9645         var tbd = this.getGridEl().select('tbody', true).first();
9646         var tfd = this.getGridEl().select('tfoot', true).first();
9647         
9648         var cw = ctr.getWidth();
9649         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9650         
9651         if (tbd) {
9652             
9653             tbd.setWidth(ctr.getWidth());
9654             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9655             // this needs fixing for various usage - currently only hydra job advers I think..
9656             //tdb.setHeight(
9657             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9658             //); 
9659             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9660             cw -= barsize;
9661         }
9662         cw = Math.max(cw, this.totalWidth);
9663         this.getGridEl().select('tbody tr',true).setWidth(cw);
9664         
9665         // resize 'expandable coloumn?
9666         
9667         return; // we doe not have a view in this design..
9668         
9669     },
9670     onBodyScroll: function()
9671     {
9672         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9673         if(this.mainHead){
9674             this.mainHead.setStyle({
9675                 'position' : 'relative',
9676                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9677             });
9678         }
9679         
9680         if(this.lazyLoad){
9681             
9682             var scrollHeight = this.mainBody.dom.scrollHeight;
9683             
9684             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9685             
9686             var height = this.mainBody.getHeight();
9687             
9688             if(scrollHeight - height == scrollTop) {
9689                 
9690                 var total = this.ds.getTotalCount();
9691                 
9692                 if(this.footer.cursor + this.footer.pageSize < total){
9693                     
9694                     this.footer.ds.load({
9695                         params : {
9696                             start : this.footer.cursor + this.footer.pageSize,
9697                             limit : this.footer.pageSize
9698                         },
9699                         add : true
9700                     });
9701                 }
9702             }
9703             
9704         }
9705     },
9706     
9707     onHeaderChange : function()
9708     {
9709         var header = this.renderHeader();
9710         var table = this.el.select('table', true).first();
9711         
9712         this.mainHead.remove();
9713         this.mainHead = table.createChild(header, this.mainBody, false);
9714         
9715         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9716             e.on('click', this.sort, this);
9717         }, this);
9718         
9719         
9720     },
9721     
9722     onHiddenChange : function(colModel, colIndex, hidden)
9723     {
9724         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9725         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9726         
9727         this.CSS.updateRule(thSelector, "display", "");
9728         this.CSS.updateRule(tdSelector, "display", "");
9729         
9730         if(hidden){
9731             this.CSS.updateRule(thSelector, "display", "none");
9732             this.CSS.updateRule(tdSelector, "display", "none");
9733         }
9734         
9735         this.onHeaderChange();
9736         this.onLoad();
9737     },
9738     
9739     setColumnWidth: function(col_index, width)
9740     {
9741         // width = "md-2 xs-2..."
9742         if(!this.colModel.config[col_index]) {
9743             return;
9744         }
9745         
9746         var w = width.split(" ");
9747         
9748         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9749         
9750         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9751         
9752         
9753         for(var j = 0; j < w.length; j++) {
9754             
9755             if(!w[j]) {
9756                 continue;
9757             }
9758             
9759             var size_cls = w[j].split("-");
9760             
9761             if(!Number.isInteger(size_cls[1] * 1)) {
9762                 continue;
9763             }
9764             
9765             if(!this.colModel.config[col_index][size_cls[0]]) {
9766                 continue;
9767             }
9768             
9769             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9770                 continue;
9771             }
9772             
9773             h_row[0].classList.replace(
9774                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9775                 "col-"+size_cls[0]+"-"+size_cls[1]
9776             );
9777             
9778             for(var i = 0; i < rows.length; i++) {
9779                 
9780                 var size_cls = w[j].split("-");
9781                 
9782                 if(!Number.isInteger(size_cls[1] * 1)) {
9783                     continue;
9784                 }
9785                 
9786                 if(!this.colModel.config[col_index][size_cls[0]]) {
9787                     continue;
9788                 }
9789                 
9790                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9791                     continue;
9792                 }
9793                 
9794                 rows[i].classList.replace(
9795                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9796                     "col-"+size_cls[0]+"-"+size_cls[1]
9797                 );
9798             }
9799             
9800             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9801         }
9802     }
9803 });
9804
9805  
9806
9807 /**
9808  * @depricated
9809 */
9810 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9811 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9812  * - LGPL
9813  *
9814  * table cell
9815  * 
9816  */
9817
9818 /**
9819  * @class Roo.bootstrap.TableCell
9820  * @extends Roo.bootstrap.Component
9821  * Bootstrap TableCell class
9822  * @cfg {String} html cell contain text
9823  * @cfg {String} cls cell class
9824  * @cfg {String} tag cell tag (td|th) default td
9825  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9826  * @cfg {String} align Aligns the content in a cell
9827  * @cfg {String} axis Categorizes cells
9828  * @cfg {String} bgcolor Specifies the background color of a cell
9829  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9830  * @cfg {Number} colspan Specifies the number of columns a cell should span
9831  * @cfg {String} headers Specifies one or more header cells a cell is related to
9832  * @cfg {Number} height Sets the height of a cell
9833  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9834  * @cfg {Number} rowspan Sets the number of rows a cell should span
9835  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9836  * @cfg {String} valign Vertical aligns the content in a cell
9837  * @cfg {Number} width Specifies the width of a cell
9838  * 
9839  * @constructor
9840  * Create a new TableCell
9841  * @param {Object} config The config object
9842  */
9843
9844 Roo.bootstrap.TableCell = function(config){
9845     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9846 };
9847
9848 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9849     
9850     html: false,
9851     cls: false,
9852     tag: false,
9853     abbr: false,
9854     align: false,
9855     axis: false,
9856     bgcolor: false,
9857     charoff: false,
9858     colspan: false,
9859     headers: false,
9860     height: false,
9861     nowrap: false,
9862     rowspan: false,
9863     scope: false,
9864     valign: false,
9865     width: false,
9866     
9867     
9868     getAutoCreate : function(){
9869         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9870         
9871         cfg = {
9872             tag: 'td'
9873         };
9874         
9875         if(this.tag){
9876             cfg.tag = this.tag;
9877         }
9878         
9879         if (this.html) {
9880             cfg.html=this.html
9881         }
9882         if (this.cls) {
9883             cfg.cls=this.cls
9884         }
9885         if (this.abbr) {
9886             cfg.abbr=this.abbr
9887         }
9888         if (this.align) {
9889             cfg.align=this.align
9890         }
9891         if (this.axis) {
9892             cfg.axis=this.axis
9893         }
9894         if (this.bgcolor) {
9895             cfg.bgcolor=this.bgcolor
9896         }
9897         if (this.charoff) {
9898             cfg.charoff=this.charoff
9899         }
9900         if (this.colspan) {
9901             cfg.colspan=this.colspan
9902         }
9903         if (this.headers) {
9904             cfg.headers=this.headers
9905         }
9906         if (this.height) {
9907             cfg.height=this.height
9908         }
9909         if (this.nowrap) {
9910             cfg.nowrap=this.nowrap
9911         }
9912         if (this.rowspan) {
9913             cfg.rowspan=this.rowspan
9914         }
9915         if (this.scope) {
9916             cfg.scope=this.scope
9917         }
9918         if (this.valign) {
9919             cfg.valign=this.valign
9920         }
9921         if (this.width) {
9922             cfg.width=this.width
9923         }
9924         
9925         
9926         return cfg;
9927     }
9928    
9929 });
9930
9931  
9932
9933  /*
9934  * - LGPL
9935  *
9936  * table row
9937  * 
9938  */
9939
9940 /**
9941  * @class Roo.bootstrap.TableRow
9942  * @extends Roo.bootstrap.Component
9943  * Bootstrap TableRow class
9944  * @cfg {String} cls row class
9945  * @cfg {String} align Aligns the content in a table row
9946  * @cfg {String} bgcolor Specifies a background color for a table row
9947  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9948  * @cfg {String} valign Vertical aligns the content in a table row
9949  * 
9950  * @constructor
9951  * Create a new TableRow
9952  * @param {Object} config The config object
9953  */
9954
9955 Roo.bootstrap.TableRow = function(config){
9956     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9957 };
9958
9959 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9960     
9961     cls: false,
9962     align: false,
9963     bgcolor: false,
9964     charoff: false,
9965     valign: false,
9966     
9967     getAutoCreate : function(){
9968         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9969         
9970         cfg = {
9971             tag: 'tr'
9972         };
9973             
9974         if(this.cls){
9975             cfg.cls = this.cls;
9976         }
9977         if(this.align){
9978             cfg.align = this.align;
9979         }
9980         if(this.bgcolor){
9981             cfg.bgcolor = this.bgcolor;
9982         }
9983         if(this.charoff){
9984             cfg.charoff = this.charoff;
9985         }
9986         if(this.valign){
9987             cfg.valign = this.valign;
9988         }
9989         
9990         return cfg;
9991     }
9992    
9993 });
9994
9995  
9996
9997  /*
9998  * - LGPL
9999  *
10000  * table body
10001  * 
10002  */
10003
10004 /**
10005  * @class Roo.bootstrap.TableBody
10006  * @extends Roo.bootstrap.Component
10007  * Bootstrap TableBody class
10008  * @cfg {String} cls element class
10009  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10010  * @cfg {String} align Aligns the content inside the element
10011  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10012  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10013  * 
10014  * @constructor
10015  * Create a new TableBody
10016  * @param {Object} config The config object
10017  */
10018
10019 Roo.bootstrap.TableBody = function(config){
10020     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10021 };
10022
10023 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10024     
10025     cls: false,
10026     tag: false,
10027     align: false,
10028     charoff: false,
10029     valign: false,
10030     
10031     getAutoCreate : function(){
10032         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10033         
10034         cfg = {
10035             tag: 'tbody'
10036         };
10037             
10038         if (this.cls) {
10039             cfg.cls=this.cls
10040         }
10041         if(this.tag){
10042             cfg.tag = this.tag;
10043         }
10044         
10045         if(this.align){
10046             cfg.align = this.align;
10047         }
10048         if(this.charoff){
10049             cfg.charoff = this.charoff;
10050         }
10051         if(this.valign){
10052             cfg.valign = this.valign;
10053         }
10054         
10055         return cfg;
10056     }
10057     
10058     
10059 //    initEvents : function()
10060 //    {
10061 //        
10062 //        if(!this.store){
10063 //            return;
10064 //        }
10065 //        
10066 //        this.store = Roo.factory(this.store, Roo.data);
10067 //        this.store.on('load', this.onLoad, this);
10068 //        
10069 //        this.store.load();
10070 //        
10071 //    },
10072 //    
10073 //    onLoad: function () 
10074 //    {   
10075 //        this.fireEvent('load', this);
10076 //    }
10077 //    
10078 //   
10079 });
10080
10081  
10082
10083  /*
10084  * Based on:
10085  * Ext JS Library 1.1.1
10086  * Copyright(c) 2006-2007, Ext JS, LLC.
10087  *
10088  * Originally Released Under LGPL - original licence link has changed is not relivant.
10089  *
10090  * Fork - LGPL
10091  * <script type="text/javascript">
10092  */
10093
10094 // as we use this in bootstrap.
10095 Roo.namespace('Roo.form');
10096  /**
10097  * @class Roo.form.Action
10098  * Internal Class used to handle form actions
10099  * @constructor
10100  * @param {Roo.form.BasicForm} el The form element or its id
10101  * @param {Object} config Configuration options
10102  */
10103
10104  
10105  
10106 // define the action interface
10107 Roo.form.Action = function(form, options){
10108     this.form = form;
10109     this.options = options || {};
10110 };
10111 /**
10112  * Client Validation Failed
10113  * @const 
10114  */
10115 Roo.form.Action.CLIENT_INVALID = 'client';
10116 /**
10117  * Server Validation Failed
10118  * @const 
10119  */
10120 Roo.form.Action.SERVER_INVALID = 'server';
10121  /**
10122  * Connect to Server Failed
10123  * @const 
10124  */
10125 Roo.form.Action.CONNECT_FAILURE = 'connect';
10126 /**
10127  * Reading Data from Server Failed
10128  * @const 
10129  */
10130 Roo.form.Action.LOAD_FAILURE = 'load';
10131
10132 Roo.form.Action.prototype = {
10133     type : 'default',
10134     failureType : undefined,
10135     response : undefined,
10136     result : undefined,
10137
10138     // interface method
10139     run : function(options){
10140
10141     },
10142
10143     // interface method
10144     success : function(response){
10145
10146     },
10147
10148     // interface method
10149     handleResponse : function(response){
10150
10151     },
10152
10153     // default connection failure
10154     failure : function(response){
10155         
10156         this.response = response;
10157         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10158         this.form.afterAction(this, false);
10159     },
10160
10161     processResponse : function(response){
10162         this.response = response;
10163         if(!response.responseText){
10164             return true;
10165         }
10166         this.result = this.handleResponse(response);
10167         return this.result;
10168     },
10169
10170     // utility functions used internally
10171     getUrl : function(appendParams){
10172         var url = this.options.url || this.form.url || this.form.el.dom.action;
10173         if(appendParams){
10174             var p = this.getParams();
10175             if(p){
10176                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10177             }
10178         }
10179         return url;
10180     },
10181
10182     getMethod : function(){
10183         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10184     },
10185
10186     getParams : function(){
10187         var bp = this.form.baseParams;
10188         var p = this.options.params;
10189         if(p){
10190             if(typeof p == "object"){
10191                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10192             }else if(typeof p == 'string' && bp){
10193                 p += '&' + Roo.urlEncode(bp);
10194             }
10195         }else if(bp){
10196             p = Roo.urlEncode(bp);
10197         }
10198         return p;
10199     },
10200
10201     createCallback : function(){
10202         return {
10203             success: this.success,
10204             failure: this.failure,
10205             scope: this,
10206             timeout: (this.form.timeout*1000),
10207             upload: this.form.fileUpload ? this.success : undefined
10208         };
10209     }
10210 };
10211
10212 Roo.form.Action.Submit = function(form, options){
10213     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10214 };
10215
10216 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10217     type : 'submit',
10218
10219     haveProgress : false,
10220     uploadComplete : false,
10221     
10222     // uploadProgress indicator.
10223     uploadProgress : function()
10224     {
10225         if (!this.form.progressUrl) {
10226             return;
10227         }
10228         
10229         if (!this.haveProgress) {
10230             Roo.MessageBox.progress("Uploading", "Uploading");
10231         }
10232         if (this.uploadComplete) {
10233            Roo.MessageBox.hide();
10234            return;
10235         }
10236         
10237         this.haveProgress = true;
10238    
10239         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10240         
10241         var c = new Roo.data.Connection();
10242         c.request({
10243             url : this.form.progressUrl,
10244             params: {
10245                 id : uid
10246             },
10247             method: 'GET',
10248             success : function(req){
10249                //console.log(data);
10250                 var rdata = false;
10251                 var edata;
10252                 try  {
10253                    rdata = Roo.decode(req.responseText)
10254                 } catch (e) {
10255                     Roo.log("Invalid data from server..");
10256                     Roo.log(edata);
10257                     return;
10258                 }
10259                 if (!rdata || !rdata.success) {
10260                     Roo.log(rdata);
10261                     Roo.MessageBox.alert(Roo.encode(rdata));
10262                     return;
10263                 }
10264                 var data = rdata.data;
10265                 
10266                 if (this.uploadComplete) {
10267                    Roo.MessageBox.hide();
10268                    return;
10269                 }
10270                    
10271                 if (data){
10272                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10273                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10274                     );
10275                 }
10276                 this.uploadProgress.defer(2000,this);
10277             },
10278        
10279             failure: function(data) {
10280                 Roo.log('progress url failed ');
10281                 Roo.log(data);
10282             },
10283             scope : this
10284         });
10285            
10286     },
10287     
10288     
10289     run : function()
10290     {
10291         // run get Values on the form, so it syncs any secondary forms.
10292         this.form.getValues();
10293         
10294         var o = this.options;
10295         var method = this.getMethod();
10296         var isPost = method == 'POST';
10297         if(o.clientValidation === false || this.form.isValid()){
10298             
10299             if (this.form.progressUrl) {
10300                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10301                     (new Date() * 1) + '' + Math.random());
10302                     
10303             } 
10304             
10305             
10306             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10307                 form:this.form.el.dom,
10308                 url:this.getUrl(!isPost),
10309                 method: method,
10310                 params:isPost ? this.getParams() : null,
10311                 isUpload: this.form.fileUpload,
10312                 formData : this.form.formData
10313             }));
10314             
10315             this.uploadProgress();
10316
10317         }else if (o.clientValidation !== false){ // client validation failed
10318             this.failureType = Roo.form.Action.CLIENT_INVALID;
10319             this.form.afterAction(this, false);
10320         }
10321     },
10322
10323     success : function(response)
10324     {
10325         this.uploadComplete= true;
10326         if (this.haveProgress) {
10327             Roo.MessageBox.hide();
10328         }
10329         
10330         
10331         var result = this.processResponse(response);
10332         if(result === true || result.success){
10333             this.form.afterAction(this, true);
10334             return;
10335         }
10336         if(result.errors){
10337             this.form.markInvalid(result.errors);
10338             this.failureType = Roo.form.Action.SERVER_INVALID;
10339         }
10340         this.form.afterAction(this, false);
10341     },
10342     failure : function(response)
10343     {
10344         this.uploadComplete= true;
10345         if (this.haveProgress) {
10346             Roo.MessageBox.hide();
10347         }
10348         
10349         this.response = response;
10350         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10351         this.form.afterAction(this, false);
10352     },
10353     
10354     handleResponse : function(response){
10355         if(this.form.errorReader){
10356             var rs = this.form.errorReader.read(response);
10357             var errors = [];
10358             if(rs.records){
10359                 for(var i = 0, len = rs.records.length; i < len; i++) {
10360                     var r = rs.records[i];
10361                     errors[i] = r.data;
10362                 }
10363             }
10364             if(errors.length < 1){
10365                 errors = null;
10366             }
10367             return {
10368                 success : rs.success,
10369                 errors : errors
10370             };
10371         }
10372         var ret = false;
10373         try {
10374             ret = Roo.decode(response.responseText);
10375         } catch (e) {
10376             ret = {
10377                 success: false,
10378                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10379                 errors : []
10380             };
10381         }
10382         return ret;
10383         
10384     }
10385 });
10386
10387
10388 Roo.form.Action.Load = function(form, options){
10389     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10390     this.reader = this.form.reader;
10391 };
10392
10393 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10394     type : 'load',
10395
10396     run : function(){
10397         
10398         Roo.Ajax.request(Roo.apply(
10399                 this.createCallback(), {
10400                     method:this.getMethod(),
10401                     url:this.getUrl(false),
10402                     params:this.getParams()
10403         }));
10404     },
10405
10406     success : function(response){
10407         
10408         var result = this.processResponse(response);
10409         if(result === true || !result.success || !result.data){
10410             this.failureType = Roo.form.Action.LOAD_FAILURE;
10411             this.form.afterAction(this, false);
10412             return;
10413         }
10414         this.form.clearInvalid();
10415         this.form.setValues(result.data);
10416         this.form.afterAction(this, true);
10417     },
10418
10419     handleResponse : function(response){
10420         if(this.form.reader){
10421             var rs = this.form.reader.read(response);
10422             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10423             return {
10424                 success : rs.success,
10425                 data : data
10426             };
10427         }
10428         return Roo.decode(response.responseText);
10429     }
10430 });
10431
10432 Roo.form.Action.ACTION_TYPES = {
10433     'load' : Roo.form.Action.Load,
10434     'submit' : Roo.form.Action.Submit
10435 };/*
10436  * - LGPL
10437  *
10438  * form
10439  *
10440  */
10441
10442 /**
10443  * @class Roo.bootstrap.Form
10444  * @extends Roo.bootstrap.Component
10445  * Bootstrap Form class
10446  * @cfg {String} method  GET | POST (default POST)
10447  * @cfg {String} labelAlign top | left (default top)
10448  * @cfg {String} align left  | right - for navbars
10449  * @cfg {Boolean} loadMask load mask when submit (default true)
10450
10451  *
10452  * @constructor
10453  * Create a new Form
10454  * @param {Object} config The config object
10455  */
10456
10457
10458 Roo.bootstrap.Form = function(config){
10459     
10460     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10461     
10462     Roo.bootstrap.Form.popover.apply();
10463     
10464     this.addEvents({
10465         /**
10466          * @event clientvalidation
10467          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10468          * @param {Form} this
10469          * @param {Boolean} valid true if the form has passed client-side validation
10470          */
10471         clientvalidation: true,
10472         /**
10473          * @event beforeaction
10474          * Fires before any action is performed. Return false to cancel the action.
10475          * @param {Form} this
10476          * @param {Action} action The action to be performed
10477          */
10478         beforeaction: true,
10479         /**
10480          * @event actionfailed
10481          * Fires when an action fails.
10482          * @param {Form} this
10483          * @param {Action} action The action that failed
10484          */
10485         actionfailed : true,
10486         /**
10487          * @event actioncomplete
10488          * Fires when an action is completed.
10489          * @param {Form} this
10490          * @param {Action} action The action that completed
10491          */
10492         actioncomplete : true
10493     });
10494 };
10495
10496 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10497
10498      /**
10499      * @cfg {String} method
10500      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10501      */
10502     method : 'POST',
10503     /**
10504      * @cfg {String} url
10505      * The URL to use for form actions if one isn't supplied in the action options.
10506      */
10507     /**
10508      * @cfg {Boolean} fileUpload
10509      * Set to true if this form is a file upload.
10510      */
10511
10512     /**
10513      * @cfg {Object} baseParams
10514      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10515      */
10516
10517     /**
10518      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10519      */
10520     timeout: 30,
10521     /**
10522      * @cfg {Sting} align (left|right) for navbar forms
10523      */
10524     align : 'left',
10525
10526     // private
10527     activeAction : null,
10528
10529     /**
10530      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10531      * element by passing it or its id or mask the form itself by passing in true.
10532      * @type Mixed
10533      */
10534     waitMsgTarget : false,
10535
10536     loadMask : true,
10537     
10538     /**
10539      * @cfg {Boolean} errorMask (true|false) default false
10540      */
10541     errorMask : false,
10542     
10543     /**
10544      * @cfg {Number} maskOffset Default 100
10545      */
10546     maskOffset : 100,
10547     
10548     /**
10549      * @cfg {Boolean} maskBody
10550      */
10551     maskBody : false,
10552
10553     getAutoCreate : function(){
10554
10555         var cfg = {
10556             tag: 'form',
10557             method : this.method || 'POST',
10558             id : this.id || Roo.id(),
10559             cls : ''
10560         };
10561         if (this.parent().xtype.match(/^Nav/)) {
10562             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10563
10564         }
10565
10566         if (this.labelAlign == 'left' ) {
10567             cfg.cls += ' form-horizontal';
10568         }
10569
10570
10571         return cfg;
10572     },
10573     initEvents : function()
10574     {
10575         this.el.on('submit', this.onSubmit, this);
10576         // this was added as random key presses on the form where triggering form submit.
10577         this.el.on('keypress', function(e) {
10578             if (e.getCharCode() != 13) {
10579                 return true;
10580             }
10581             // we might need to allow it for textareas.. and some other items.
10582             // check e.getTarget().
10583
10584             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10585                 return true;
10586             }
10587
10588             Roo.log("keypress blocked");
10589
10590             e.preventDefault();
10591             return false;
10592         });
10593         
10594     },
10595     // private
10596     onSubmit : function(e){
10597         e.stopEvent();
10598     },
10599
10600      /**
10601      * Returns true if client-side validation on the form is successful.
10602      * @return Boolean
10603      */
10604     isValid : function(){
10605         var items = this.getItems();
10606         var valid = true;
10607         var target = false;
10608         
10609         items.each(function(f){
10610             
10611             if(f.validate()){
10612                 return;
10613             }
10614             
10615             Roo.log('invalid field: ' + f.name);
10616             
10617             valid = false;
10618
10619             if(!target && f.el.isVisible(true)){
10620                 target = f;
10621             }
10622            
10623         });
10624         
10625         if(this.errorMask && !valid){
10626             Roo.bootstrap.Form.popover.mask(this, target);
10627         }
10628         
10629         return valid;
10630     },
10631     
10632     /**
10633      * Returns true if any fields in this form have changed since their original load.
10634      * @return Boolean
10635      */
10636     isDirty : function(){
10637         var dirty = false;
10638         var items = this.getItems();
10639         items.each(function(f){
10640            if(f.isDirty()){
10641                dirty = true;
10642                return false;
10643            }
10644            return true;
10645         });
10646         return dirty;
10647     },
10648      /**
10649      * Performs a predefined action (submit or load) or custom actions you define on this form.
10650      * @param {String} actionName The name of the action type
10651      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10652      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10653      * accept other config options):
10654      * <pre>
10655 Property          Type             Description
10656 ----------------  ---------------  ----------------------------------------------------------------------------------
10657 url               String           The url for the action (defaults to the form's url)
10658 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10659 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10660 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10661                                    validate the form on the client (defaults to false)
10662      * </pre>
10663      * @return {BasicForm} this
10664      */
10665     doAction : function(action, options){
10666         if(typeof action == 'string'){
10667             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10668         }
10669         if(this.fireEvent('beforeaction', this, action) !== false){
10670             this.beforeAction(action);
10671             action.run.defer(100, action);
10672         }
10673         return this;
10674     },
10675
10676     // private
10677     beforeAction : function(action){
10678         var o = action.options;
10679         
10680         if(this.loadMask){
10681             
10682             if(this.maskBody){
10683                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10684             } else {
10685                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10686             }
10687         }
10688         // not really supported yet.. ??
10689
10690         //if(this.waitMsgTarget === true){
10691         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10692         //}else if(this.waitMsgTarget){
10693         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10694         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10695         //}else {
10696         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10697        // }
10698
10699     },
10700
10701     // private
10702     afterAction : function(action, success){
10703         this.activeAction = null;
10704         var o = action.options;
10705
10706         if(this.loadMask){
10707             
10708             if(this.maskBody){
10709                 Roo.get(document.body).unmask();
10710             } else {
10711                 this.el.unmask();
10712             }
10713         }
10714         
10715         //if(this.waitMsgTarget === true){
10716 //            this.el.unmask();
10717         //}else if(this.waitMsgTarget){
10718         //    this.waitMsgTarget.unmask();
10719         //}else{
10720         //    Roo.MessageBox.updateProgress(1);
10721         //    Roo.MessageBox.hide();
10722        // }
10723         //
10724         if(success){
10725             if(o.reset){
10726                 this.reset();
10727             }
10728             Roo.callback(o.success, o.scope, [this, action]);
10729             this.fireEvent('actioncomplete', this, action);
10730
10731         }else{
10732
10733             // failure condition..
10734             // we have a scenario where updates need confirming.
10735             // eg. if a locking scenario exists..
10736             // we look for { errors : { needs_confirm : true }} in the response.
10737             if (
10738                 (typeof(action.result) != 'undefined')  &&
10739                 (typeof(action.result.errors) != 'undefined')  &&
10740                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10741            ){
10742                 var _t = this;
10743                 Roo.log("not supported yet");
10744                  /*
10745
10746                 Roo.MessageBox.confirm(
10747                     "Change requires confirmation",
10748                     action.result.errorMsg,
10749                     function(r) {
10750                         if (r != 'yes') {
10751                             return;
10752                         }
10753                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10754                     }
10755
10756                 );
10757                 */
10758
10759
10760                 return;
10761             }
10762
10763             Roo.callback(o.failure, o.scope, [this, action]);
10764             // show an error message if no failed handler is set..
10765             if (!this.hasListener('actionfailed')) {
10766                 Roo.log("need to add dialog support");
10767                 /*
10768                 Roo.MessageBox.alert("Error",
10769                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10770                         action.result.errorMsg :
10771                         "Saving Failed, please check your entries or try again"
10772                 );
10773                 */
10774             }
10775
10776             this.fireEvent('actionfailed', this, action);
10777         }
10778
10779     },
10780     /**
10781      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10782      * @param {String} id The value to search for
10783      * @return Field
10784      */
10785     findField : function(id){
10786         var items = this.getItems();
10787         var field = items.get(id);
10788         if(!field){
10789              items.each(function(f){
10790                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10791                     field = f;
10792                     return false;
10793                 }
10794                 return true;
10795             });
10796         }
10797         return field || null;
10798     },
10799      /**
10800      * Mark fields in this form invalid in bulk.
10801      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10802      * @return {BasicForm} this
10803      */
10804     markInvalid : function(errors){
10805         if(errors instanceof Array){
10806             for(var i = 0, len = errors.length; i < len; i++){
10807                 var fieldError = errors[i];
10808                 var f = this.findField(fieldError.id);
10809                 if(f){
10810                     f.markInvalid(fieldError.msg);
10811                 }
10812             }
10813         }else{
10814             var field, id;
10815             for(id in errors){
10816                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10817                     field.markInvalid(errors[id]);
10818                 }
10819             }
10820         }
10821         //Roo.each(this.childForms || [], function (f) {
10822         //    f.markInvalid(errors);
10823         //});
10824
10825         return this;
10826     },
10827
10828     /**
10829      * Set values for fields in this form in bulk.
10830      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10831      * @return {BasicForm} this
10832      */
10833     setValues : function(values){
10834         if(values instanceof Array){ // array of objects
10835             for(var i = 0, len = values.length; i < len; i++){
10836                 var v = values[i];
10837                 var f = this.findField(v.id);
10838                 if(f){
10839                     f.setValue(v.value);
10840                     if(this.trackResetOnLoad){
10841                         f.originalValue = f.getValue();
10842                     }
10843                 }
10844             }
10845         }else{ // object hash
10846             var field, id;
10847             for(id in values){
10848                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10849
10850                     if (field.setFromData &&
10851                         field.valueField &&
10852                         field.displayField &&
10853                         // combos' with local stores can
10854                         // be queried via setValue()
10855                         // to set their value..
10856                         (field.store && !field.store.isLocal)
10857                         ) {
10858                         // it's a combo
10859                         var sd = { };
10860                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10861                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10862                         field.setFromData(sd);
10863
10864                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10865                         
10866                         field.setFromData(values);
10867                         
10868                     } else {
10869                         field.setValue(values[id]);
10870                     }
10871
10872
10873                     if(this.trackResetOnLoad){
10874                         field.originalValue = field.getValue();
10875                     }
10876                 }
10877             }
10878         }
10879
10880         //Roo.each(this.childForms || [], function (f) {
10881         //    f.setValues(values);
10882         //});
10883
10884         return this;
10885     },
10886
10887     /**
10888      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10889      * they are returned as an array.
10890      * @param {Boolean} asString
10891      * @return {Object}
10892      */
10893     getValues : function(asString){
10894         //if (this.childForms) {
10895             // copy values from the child forms
10896         //    Roo.each(this.childForms, function (f) {
10897         //        this.setValues(f.getValues());
10898         //    }, this);
10899         //}
10900
10901
10902
10903         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10904         if(asString === true){
10905             return fs;
10906         }
10907         return Roo.urlDecode(fs);
10908     },
10909
10910     /**
10911      * Returns the fields in this form as an object with key/value pairs.
10912      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10913      * @return {Object}
10914      */
10915     getFieldValues : function(with_hidden)
10916     {
10917         var items = this.getItems();
10918         var ret = {};
10919         items.each(function(f){
10920             
10921             if (!f.getName()) {
10922                 return;
10923             }
10924             
10925             var v = f.getValue();
10926             
10927             if (f.inputType =='radio') {
10928                 if (typeof(ret[f.getName()]) == 'undefined') {
10929                     ret[f.getName()] = ''; // empty..
10930                 }
10931
10932                 if (!f.el.dom.checked) {
10933                     return;
10934
10935                 }
10936                 v = f.el.dom.value;
10937
10938             }
10939             
10940             if(f.xtype == 'MoneyField'){
10941                 ret[f.currencyName] = f.getCurrency();
10942             }
10943
10944             // not sure if this supported any more..
10945             if ((typeof(v) == 'object') && f.getRawValue) {
10946                 v = f.getRawValue() ; // dates..
10947             }
10948             // combo boxes where name != hiddenName...
10949             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10950                 ret[f.name] = f.getRawValue();
10951             }
10952             ret[f.getName()] = v;
10953         });
10954
10955         return ret;
10956     },
10957
10958     /**
10959      * Clears all invalid messages in this form.
10960      * @return {BasicForm} this
10961      */
10962     clearInvalid : function(){
10963         var items = this.getItems();
10964
10965         items.each(function(f){
10966            f.clearInvalid();
10967         });
10968
10969         return this;
10970     },
10971
10972     /**
10973      * Resets this form.
10974      * @return {BasicForm} this
10975      */
10976     reset : function(){
10977         var items = this.getItems();
10978         items.each(function(f){
10979             f.reset();
10980         });
10981
10982         Roo.each(this.childForms || [], function (f) {
10983             f.reset();
10984         });
10985
10986
10987         return this;
10988     },
10989     
10990     getItems : function()
10991     {
10992         var r=new Roo.util.MixedCollection(false, function(o){
10993             return o.id || (o.id = Roo.id());
10994         });
10995         var iter = function(el) {
10996             if (el.inputEl) {
10997                 r.add(el);
10998             }
10999             if (!el.items) {
11000                 return;
11001             }
11002             Roo.each(el.items,function(e) {
11003                 iter(e);
11004             });
11005         };
11006
11007         iter(this);
11008         return r;
11009     },
11010     
11011     hideFields : function(items)
11012     {
11013         Roo.each(items, function(i){
11014             
11015             var f = this.findField(i);
11016             
11017             if(!f){
11018                 return;
11019             }
11020             
11021             f.hide();
11022             
11023         }, this);
11024     },
11025     
11026     showFields : function(items)
11027     {
11028         Roo.each(items, function(i){
11029             
11030             var f = this.findField(i);
11031             
11032             if(!f){
11033                 return;
11034             }
11035             
11036             f.show();
11037             
11038         }, this);
11039     }
11040
11041 });
11042
11043 Roo.apply(Roo.bootstrap.Form, {
11044     
11045     popover : {
11046         
11047         padding : 5,
11048         
11049         isApplied : false,
11050         
11051         isMasked : false,
11052         
11053         form : false,
11054         
11055         target : false,
11056         
11057         toolTip : false,
11058         
11059         intervalID : false,
11060         
11061         maskEl : false,
11062         
11063         apply : function()
11064         {
11065             if(this.isApplied){
11066                 return;
11067             }
11068             
11069             this.maskEl = {
11070                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11071                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11072                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11073                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11074             };
11075             
11076             this.maskEl.top.enableDisplayMode("block");
11077             this.maskEl.left.enableDisplayMode("block");
11078             this.maskEl.bottom.enableDisplayMode("block");
11079             this.maskEl.right.enableDisplayMode("block");
11080             
11081             this.toolTip = new Roo.bootstrap.Tooltip({
11082                 cls : 'roo-form-error-popover',
11083                 alignment : {
11084                     'left' : ['r-l', [-2,0], 'right'],
11085                     'right' : ['l-r', [2,0], 'left'],
11086                     'bottom' : ['tl-bl', [0,2], 'top'],
11087                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11088                 }
11089             });
11090             
11091             this.toolTip.render(Roo.get(document.body));
11092
11093             this.toolTip.el.enableDisplayMode("block");
11094             
11095             Roo.get(document.body).on('click', function(){
11096                 this.unmask();
11097             }, this);
11098             
11099             Roo.get(document.body).on('touchstart', function(){
11100                 this.unmask();
11101             }, this);
11102             
11103             this.isApplied = true
11104         },
11105         
11106         mask : function(form, target)
11107         {
11108             this.form = form;
11109             
11110             this.target = target;
11111             
11112             if(!this.form.errorMask || !target.el){
11113                 return;
11114             }
11115             
11116             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11117             
11118             Roo.log(scrollable);
11119             
11120             var ot = this.target.el.calcOffsetsTo(scrollable);
11121             
11122             var scrollTo = ot[1] - this.form.maskOffset;
11123             
11124             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11125             
11126             scrollable.scrollTo('top', scrollTo);
11127             
11128             var box = this.target.el.getBox();
11129             Roo.log(box);
11130             var zIndex = Roo.bootstrap.Modal.zIndex++;
11131
11132             
11133             this.maskEl.top.setStyle('position', 'absolute');
11134             this.maskEl.top.setStyle('z-index', zIndex);
11135             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11136             this.maskEl.top.setLeft(0);
11137             this.maskEl.top.setTop(0);
11138             this.maskEl.top.show();
11139             
11140             this.maskEl.left.setStyle('position', 'absolute');
11141             this.maskEl.left.setStyle('z-index', zIndex);
11142             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11143             this.maskEl.left.setLeft(0);
11144             this.maskEl.left.setTop(box.y - this.padding);
11145             this.maskEl.left.show();
11146
11147             this.maskEl.bottom.setStyle('position', 'absolute');
11148             this.maskEl.bottom.setStyle('z-index', zIndex);
11149             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11150             this.maskEl.bottom.setLeft(0);
11151             this.maskEl.bottom.setTop(box.bottom + this.padding);
11152             this.maskEl.bottom.show();
11153
11154             this.maskEl.right.setStyle('position', 'absolute');
11155             this.maskEl.right.setStyle('z-index', zIndex);
11156             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11157             this.maskEl.right.setLeft(box.right + this.padding);
11158             this.maskEl.right.setTop(box.y - this.padding);
11159             this.maskEl.right.show();
11160
11161             this.toolTip.bindEl = this.target.el;
11162
11163             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11164
11165             var tip = this.target.blankText;
11166
11167             if(this.target.getValue() !== '' ) {
11168                 
11169                 if (this.target.invalidText.length) {
11170                     tip = this.target.invalidText;
11171                 } else if (this.target.regexText.length){
11172                     tip = this.target.regexText;
11173                 }
11174             }
11175
11176             this.toolTip.show(tip);
11177
11178             this.intervalID = window.setInterval(function() {
11179                 Roo.bootstrap.Form.popover.unmask();
11180             }, 10000);
11181
11182             window.onwheel = function(){ return false;};
11183             
11184             (function(){ this.isMasked = true; }).defer(500, this);
11185             
11186         },
11187         
11188         unmask : function()
11189         {
11190             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11191                 return;
11192             }
11193             
11194             this.maskEl.top.setStyle('position', 'absolute');
11195             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11196             this.maskEl.top.hide();
11197
11198             this.maskEl.left.setStyle('position', 'absolute');
11199             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11200             this.maskEl.left.hide();
11201
11202             this.maskEl.bottom.setStyle('position', 'absolute');
11203             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11204             this.maskEl.bottom.hide();
11205
11206             this.maskEl.right.setStyle('position', 'absolute');
11207             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11208             this.maskEl.right.hide();
11209             
11210             this.toolTip.hide();
11211             
11212             this.toolTip.el.hide();
11213             
11214             window.onwheel = function(){ return true;};
11215             
11216             if(this.intervalID){
11217                 window.clearInterval(this.intervalID);
11218                 this.intervalID = false;
11219             }
11220             
11221             this.isMasked = false;
11222             
11223         }
11224         
11225     }
11226     
11227 });
11228
11229 /*
11230  * Based on:
11231  * Ext JS Library 1.1.1
11232  * Copyright(c) 2006-2007, Ext JS, LLC.
11233  *
11234  * Originally Released Under LGPL - original licence link has changed is not relivant.
11235  *
11236  * Fork - LGPL
11237  * <script type="text/javascript">
11238  */
11239 /**
11240  * @class Roo.form.VTypes
11241  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11242  * @singleton
11243  */
11244 Roo.form.VTypes = function(){
11245     // closure these in so they are only created once.
11246     var alpha = /^[a-zA-Z_]+$/;
11247     var alphanum = /^[a-zA-Z0-9_]+$/;
11248     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11249     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11250
11251     // All these messages and functions are configurable
11252     return {
11253         /**
11254          * The function used to validate email addresses
11255          * @param {String} value The email address
11256          */
11257         'email' : function(v){
11258             return email.test(v);
11259         },
11260         /**
11261          * The error text to display when the email validation function returns false
11262          * @type String
11263          */
11264         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11265         /**
11266          * The keystroke filter mask to be applied on email input
11267          * @type RegExp
11268          */
11269         'emailMask' : /[a-z0-9_\.\-@]/i,
11270
11271         /**
11272          * The function used to validate URLs
11273          * @param {String} value The URL
11274          */
11275         'url' : function(v){
11276             return url.test(v);
11277         },
11278         /**
11279          * The error text to display when the url validation function returns false
11280          * @type String
11281          */
11282         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11283         
11284         /**
11285          * The function used to validate alpha values
11286          * @param {String} value The value
11287          */
11288         'alpha' : function(v){
11289             return alpha.test(v);
11290         },
11291         /**
11292          * The error text to display when the alpha validation function returns false
11293          * @type String
11294          */
11295         'alphaText' : 'This field should only contain letters and _',
11296         /**
11297          * The keystroke filter mask to be applied on alpha input
11298          * @type RegExp
11299          */
11300         'alphaMask' : /[a-z_]/i,
11301
11302         /**
11303          * The function used to validate alphanumeric values
11304          * @param {String} value The value
11305          */
11306         'alphanum' : function(v){
11307             return alphanum.test(v);
11308         },
11309         /**
11310          * The error text to display when the alphanumeric validation function returns false
11311          * @type String
11312          */
11313         'alphanumText' : 'This field should only contain letters, numbers and _',
11314         /**
11315          * The keystroke filter mask to be applied on alphanumeric input
11316          * @type RegExp
11317          */
11318         'alphanumMask' : /[a-z0-9_]/i
11319     };
11320 }();/*
11321  * - LGPL
11322  *
11323  * Input
11324  * 
11325  */
11326
11327 /**
11328  * @class Roo.bootstrap.Input
11329  * @extends Roo.bootstrap.Component
11330  * Bootstrap Input class
11331  * @cfg {Boolean} disabled is it disabled
11332  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11333  * @cfg {String} name name of the input
11334  * @cfg {string} fieldLabel - the label associated
11335  * @cfg {string} placeholder - placeholder to put in text.
11336  * @cfg {string}  before - input group add on before
11337  * @cfg {string} after - input group add on after
11338  * @cfg {string} size - (lg|sm) or leave empty..
11339  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11340  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11341  * @cfg {Number} md colspan out of 12 for computer-sized screens
11342  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11343  * @cfg {string} value default value of the input
11344  * @cfg {Number} labelWidth set the width of label 
11345  * @cfg {Number} labellg set the width of label (1-12)
11346  * @cfg {Number} labelmd set the width of label (1-12)
11347  * @cfg {Number} labelsm set the width of label (1-12)
11348  * @cfg {Number} labelxs set the width of label (1-12)
11349  * @cfg {String} labelAlign (top|left)
11350  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11351  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11352  * @cfg {String} indicatorpos (left|right) default left
11353  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11354  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11355  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11356
11357  * @cfg {String} align (left|center|right) Default left
11358  * @cfg {Boolean} forceFeedback (true|false) Default false
11359  * 
11360  * @constructor
11361  * Create a new Input
11362  * @param {Object} config The config object
11363  */
11364
11365 Roo.bootstrap.Input = function(config){
11366     
11367     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11368     
11369     this.addEvents({
11370         /**
11371          * @event focus
11372          * Fires when this field receives input focus.
11373          * @param {Roo.form.Field} this
11374          */
11375         focus : true,
11376         /**
11377          * @event blur
11378          * Fires when this field loses input focus.
11379          * @param {Roo.form.Field} this
11380          */
11381         blur : true,
11382         /**
11383          * @event specialkey
11384          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11385          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11386          * @param {Roo.form.Field} this
11387          * @param {Roo.EventObject} e The event object
11388          */
11389         specialkey : true,
11390         /**
11391          * @event change
11392          * Fires just before the field blurs if the field value has changed.
11393          * @param {Roo.form.Field} this
11394          * @param {Mixed} newValue The new value
11395          * @param {Mixed} oldValue The original value
11396          */
11397         change : true,
11398         /**
11399          * @event invalid
11400          * Fires after the field has been marked as invalid.
11401          * @param {Roo.form.Field} this
11402          * @param {String} msg The validation message
11403          */
11404         invalid : true,
11405         /**
11406          * @event valid
11407          * Fires after the field has been validated with no errors.
11408          * @param {Roo.form.Field} this
11409          */
11410         valid : true,
11411          /**
11412          * @event keyup
11413          * Fires after the key up
11414          * @param {Roo.form.Field} this
11415          * @param {Roo.EventObject}  e The event Object
11416          */
11417         keyup : true,
11418         /**
11419          * @event paste
11420          * Fires after the user pastes into input
11421          * @param {Roo.form.Field} this
11422          * @param {Roo.EventObject}  e The event Object
11423          */
11424         paste : true
11425     });
11426 };
11427
11428 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11429      /**
11430      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11431       automatic validation (defaults to "keyup").
11432      */
11433     validationEvent : "keyup",
11434      /**
11435      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11436      */
11437     validateOnBlur : true,
11438     /**
11439      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11440      */
11441     validationDelay : 250,
11442      /**
11443      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11444      */
11445     focusClass : "x-form-focus",  // not needed???
11446     
11447        
11448     /**
11449      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11450      */
11451     invalidClass : "has-warning",
11452     
11453     /**
11454      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11455      */
11456     validClass : "has-success",
11457     
11458     /**
11459      * @cfg {Boolean} hasFeedback (true|false) default true
11460      */
11461     hasFeedback : true,
11462     
11463     /**
11464      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11465      */
11466     invalidFeedbackClass : "glyphicon-warning-sign",
11467     
11468     /**
11469      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11470      */
11471     validFeedbackClass : "glyphicon-ok",
11472     
11473     /**
11474      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11475      */
11476     selectOnFocus : false,
11477     
11478      /**
11479      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11480      */
11481     maskRe : null,
11482        /**
11483      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11484      */
11485     vtype : null,
11486     
11487       /**
11488      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11489      */
11490     disableKeyFilter : false,
11491     
11492        /**
11493      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11494      */
11495     disabled : false,
11496      /**
11497      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11498      */
11499     allowBlank : true,
11500     /**
11501      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11502      */
11503     blankText : "Please complete this mandatory field",
11504     
11505      /**
11506      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11507      */
11508     minLength : 0,
11509     /**
11510      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11511      */
11512     maxLength : Number.MAX_VALUE,
11513     /**
11514      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11515      */
11516     minLengthText : "The minimum length for this field is {0}",
11517     /**
11518      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11519      */
11520     maxLengthText : "The maximum length for this field is {0}",
11521   
11522     
11523     /**
11524      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11525      * If available, this function will be called only after the basic validators all return true, and will be passed the
11526      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11527      */
11528     validator : null,
11529     /**
11530      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11531      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11532      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11533      */
11534     regex : null,
11535     /**
11536      * @cfg {String} regexText -- Depricated - use Invalid Text
11537      */
11538     regexText : "",
11539     
11540     /**
11541      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11542      */
11543     invalidText : "",
11544     
11545     
11546     
11547     autocomplete: false,
11548     
11549     
11550     fieldLabel : '',
11551     inputType : 'text',
11552     
11553     name : false,
11554     placeholder: false,
11555     before : false,
11556     after : false,
11557     size : false,
11558     hasFocus : false,
11559     preventMark: false,
11560     isFormField : true,
11561     value : '',
11562     labelWidth : 2,
11563     labelAlign : false,
11564     readOnly : false,
11565     align : false,
11566     formatedValue : false,
11567     forceFeedback : false,
11568     
11569     indicatorpos : 'left',
11570     
11571     labellg : 0,
11572     labelmd : 0,
11573     labelsm : 0,
11574     labelxs : 0,
11575     
11576     capture : '',
11577     accept : '',
11578     
11579     parentLabelAlign : function()
11580     {
11581         var parent = this;
11582         while (parent.parent()) {
11583             parent = parent.parent();
11584             if (typeof(parent.labelAlign) !='undefined') {
11585                 return parent.labelAlign;
11586             }
11587         }
11588         return 'left';
11589         
11590     },
11591     
11592     getAutoCreate : function()
11593     {
11594         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11595         
11596         var id = Roo.id();
11597         
11598         var cfg = {};
11599         
11600         if(this.inputType != 'hidden'){
11601             cfg.cls = 'form-group' //input-group
11602         }
11603         
11604         var input =  {
11605             tag: 'input',
11606             id : id,
11607             type : this.inputType,
11608             value : this.value,
11609             cls : 'form-control',
11610             placeholder : this.placeholder || '',
11611             autocomplete : this.autocomplete || 'new-password'
11612         };
11613         if (this.inputType == 'file') {
11614             input.style = 'overflow:hidden'; // why not in CSS?
11615         }
11616         
11617         if(this.capture.length){
11618             input.capture = this.capture;
11619         }
11620         
11621         if(this.accept.length){
11622             input.accept = this.accept + "/*";
11623         }
11624         
11625         if(this.align){
11626             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11627         }
11628         
11629         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11630             input.maxLength = this.maxLength;
11631         }
11632         
11633         if (this.disabled) {
11634             input.disabled=true;
11635         }
11636         
11637         if (this.readOnly) {
11638             input.readonly=true;
11639         }
11640         
11641         if (this.name) {
11642             input.name = this.name;
11643         }
11644         
11645         if (this.size) {
11646             input.cls += ' input-' + this.size;
11647         }
11648         
11649         var settings=this;
11650         ['xs','sm','md','lg'].map(function(size){
11651             if (settings[size]) {
11652                 cfg.cls += ' col-' + size + '-' + settings[size];
11653             }
11654         });
11655         
11656         var inputblock = input;
11657         
11658         var feedback = {
11659             tag: 'span',
11660             cls: 'glyphicon form-control-feedback'
11661         };
11662             
11663         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11664             
11665             inputblock = {
11666                 cls : 'has-feedback',
11667                 cn :  [
11668                     input,
11669                     feedback
11670                 ] 
11671             };  
11672         }
11673         
11674         if (this.before || this.after) {
11675             
11676             inputblock = {
11677                 cls : 'input-group',
11678                 cn :  [] 
11679             };
11680             
11681             if (this.before && typeof(this.before) == 'string') {
11682                 
11683                 inputblock.cn.push({
11684                     tag :'span',
11685                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11686                     html : this.before
11687                 });
11688             }
11689             if (this.before && typeof(this.before) == 'object') {
11690                 this.before = Roo.factory(this.before);
11691                 
11692                 inputblock.cn.push({
11693                     tag :'span',
11694                     cls : 'roo-input-before input-group-prepend   input-group-' +
11695                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11696                 });
11697             }
11698             
11699             inputblock.cn.push(input);
11700             
11701             if (this.after && typeof(this.after) == 'string') {
11702                 inputblock.cn.push({
11703                     tag :'span',
11704                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11705                     html : this.after
11706                 });
11707             }
11708             if (this.after && typeof(this.after) == 'object') {
11709                 this.after = Roo.factory(this.after);
11710                 
11711                 inputblock.cn.push({
11712                     tag :'span',
11713                     cls : 'roo-input-after input-group-append  input-group-' +
11714                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11715                 });
11716             }
11717             
11718             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11719                 inputblock.cls += ' has-feedback';
11720                 inputblock.cn.push(feedback);
11721             }
11722         };
11723         var indicator = {
11724             tag : 'i',
11725             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11726             tooltip : 'This field is required'
11727         };
11728         if (this.allowBlank ) {
11729             indicator.style = this.allowBlank ? ' display:none' : '';
11730         }
11731         if (align ==='left' && this.fieldLabel.length) {
11732             
11733             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11734             
11735             cfg.cn = [
11736                 indicator,
11737                 {
11738                     tag: 'label',
11739                     'for' :  id,
11740                     cls : 'control-label col-form-label',
11741                     html : this.fieldLabel
11742
11743                 },
11744                 {
11745                     cls : "", 
11746                     cn: [
11747                         inputblock
11748                     ]
11749                 }
11750             ];
11751             
11752             var labelCfg = cfg.cn[1];
11753             var contentCfg = cfg.cn[2];
11754             
11755             if(this.indicatorpos == 'right'){
11756                 cfg.cn = [
11757                     {
11758                         tag: 'label',
11759                         'for' :  id,
11760                         cls : 'control-label col-form-label',
11761                         cn : [
11762                             {
11763                                 tag : 'span',
11764                                 html : this.fieldLabel
11765                             },
11766                             indicator
11767                         ]
11768                     },
11769                     {
11770                         cls : "",
11771                         cn: [
11772                             inputblock
11773                         ]
11774                     }
11775
11776                 ];
11777                 
11778                 labelCfg = cfg.cn[0];
11779                 contentCfg = cfg.cn[1];
11780             
11781             }
11782             
11783             if(this.labelWidth > 12){
11784                 labelCfg.style = "width: " + this.labelWidth + 'px';
11785             }
11786             
11787             if(this.labelWidth < 13 && this.labelmd == 0){
11788                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11789             }
11790             
11791             if(this.labellg > 0){
11792                 labelCfg.cls += ' col-lg-' + this.labellg;
11793                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11794             }
11795             
11796             if(this.labelmd > 0){
11797                 labelCfg.cls += ' col-md-' + this.labelmd;
11798                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11799             }
11800             
11801             if(this.labelsm > 0){
11802                 labelCfg.cls += ' col-sm-' + this.labelsm;
11803                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11804             }
11805             
11806             if(this.labelxs > 0){
11807                 labelCfg.cls += ' col-xs-' + this.labelxs;
11808                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11809             }
11810             
11811             
11812         } else if ( this.fieldLabel.length) {
11813                 
11814             
11815             
11816             cfg.cn = [
11817                 {
11818                     tag : 'i',
11819                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11820                     tooltip : 'This field is required',
11821                     style : this.allowBlank ? ' display:none' : '' 
11822                 },
11823                 {
11824                     tag: 'label',
11825                    //cls : 'input-group-addon',
11826                     html : this.fieldLabel
11827
11828                 },
11829
11830                inputblock
11831
11832            ];
11833            
11834            if(this.indicatorpos == 'right'){
11835        
11836                 cfg.cn = [
11837                     {
11838                         tag: 'label',
11839                        //cls : 'input-group-addon',
11840                         html : this.fieldLabel
11841
11842                     },
11843                     {
11844                         tag : 'i',
11845                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11846                         tooltip : 'This field is required',
11847                         style : this.allowBlank ? ' display:none' : '' 
11848                     },
11849
11850                    inputblock
11851
11852                ];
11853
11854             }
11855
11856         } else {
11857             
11858             cfg.cn = [
11859
11860                     inputblock
11861
11862             ];
11863                 
11864                 
11865         };
11866         
11867         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11868            cfg.cls += ' navbar-form';
11869         }
11870         
11871         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11872             // on BS4 we do this only if not form 
11873             cfg.cls += ' navbar-form';
11874             cfg.tag = 'li';
11875         }
11876         
11877         return cfg;
11878         
11879     },
11880     /**
11881      * return the real input element.
11882      */
11883     inputEl: function ()
11884     {
11885         return this.el.select('input.form-control',true).first();
11886     },
11887     
11888     tooltipEl : function()
11889     {
11890         return this.inputEl();
11891     },
11892     
11893     indicatorEl : function()
11894     {
11895         if (Roo.bootstrap.version == 4) {
11896             return false; // not enabled in v4 yet.
11897         }
11898         
11899         var indicator = this.el.select('i.roo-required-indicator',true).first();
11900         
11901         if(!indicator){
11902             return false;
11903         }
11904         
11905         return indicator;
11906         
11907     },
11908     
11909     setDisabled : function(v)
11910     {
11911         var i  = this.inputEl().dom;
11912         if (!v) {
11913             i.removeAttribute('disabled');
11914             return;
11915             
11916         }
11917         i.setAttribute('disabled','true');
11918     },
11919     initEvents : function()
11920     {
11921           
11922         this.inputEl().on("keydown" , this.fireKey,  this);
11923         this.inputEl().on("focus", this.onFocus,  this);
11924         this.inputEl().on("blur", this.onBlur,  this);
11925         
11926         this.inputEl().relayEvent('keyup', this);
11927         this.inputEl().relayEvent('paste', this);
11928         
11929         this.indicator = this.indicatorEl();
11930         
11931         if(this.indicator){
11932             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11933         }
11934  
11935         // reference to original value for reset
11936         this.originalValue = this.getValue();
11937         //Roo.form.TextField.superclass.initEvents.call(this);
11938         if(this.validationEvent == 'keyup'){
11939             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11940             this.inputEl().on('keyup', this.filterValidation, this);
11941         }
11942         else if(this.validationEvent !== false){
11943             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11944         }
11945         
11946         if(this.selectOnFocus){
11947             this.on("focus", this.preFocus, this);
11948             
11949         }
11950         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11951             this.inputEl().on("keypress", this.filterKeys, this);
11952         } else {
11953             this.inputEl().relayEvent('keypress', this);
11954         }
11955        /* if(this.grow){
11956             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11957             this.el.on("click", this.autoSize,  this);
11958         }
11959         */
11960         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11961             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11962         }
11963         
11964         if (typeof(this.before) == 'object') {
11965             this.before.render(this.el.select('.roo-input-before',true).first());
11966         }
11967         if (typeof(this.after) == 'object') {
11968             this.after.render(this.el.select('.roo-input-after',true).first());
11969         }
11970         
11971         this.inputEl().on('change', this.onChange, this);
11972         
11973     },
11974     filterValidation : function(e){
11975         if(!e.isNavKeyPress()){
11976             this.validationTask.delay(this.validationDelay);
11977         }
11978     },
11979      /**
11980      * Validates the field value
11981      * @return {Boolean} True if the value is valid, else false
11982      */
11983     validate : function(){
11984         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11985         if(this.disabled || this.validateValue(this.getRawValue())){
11986             this.markValid();
11987             return true;
11988         }
11989         
11990         this.markInvalid();
11991         return false;
11992     },
11993     
11994     
11995     /**
11996      * Validates a value according to the field's validation rules and marks the field as invalid
11997      * if the validation fails
11998      * @param {Mixed} value The value to validate
11999      * @return {Boolean} True if the value is valid, else false
12000      */
12001     validateValue : function(value)
12002     {
12003         if(this.getVisibilityEl().hasClass('hidden')){
12004             return true;
12005         }
12006         
12007         if(value.length < 1)  { // if it's blank
12008             if(this.allowBlank){
12009                 return true;
12010             }
12011             return false;
12012         }
12013         
12014         if(value.length < this.minLength){
12015             return false;
12016         }
12017         if(value.length > this.maxLength){
12018             return false;
12019         }
12020         if(this.vtype){
12021             var vt = Roo.form.VTypes;
12022             if(!vt[this.vtype](value, this)){
12023                 return false;
12024             }
12025         }
12026         if(typeof this.validator == "function"){
12027             var msg = this.validator(value);
12028             if(msg !== true){
12029                 return false;
12030             }
12031             if (typeof(msg) == 'string') {
12032                 this.invalidText = msg;
12033             }
12034         }
12035         
12036         if(this.regex && !this.regex.test(value)){
12037             return false;
12038         }
12039         
12040         return true;
12041     },
12042     
12043      // private
12044     fireKey : function(e){
12045         //Roo.log('field ' + e.getKey());
12046         if(e.isNavKeyPress()){
12047             this.fireEvent("specialkey", this, e);
12048         }
12049     },
12050     focus : function (selectText){
12051         if(this.rendered){
12052             this.inputEl().focus();
12053             if(selectText === true){
12054                 this.inputEl().dom.select();
12055             }
12056         }
12057         return this;
12058     } ,
12059     
12060     onFocus : function(){
12061         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12062            // this.el.addClass(this.focusClass);
12063         }
12064         if(!this.hasFocus){
12065             this.hasFocus = true;
12066             this.startValue = this.getValue();
12067             this.fireEvent("focus", this);
12068         }
12069     },
12070     
12071     beforeBlur : Roo.emptyFn,
12072
12073     
12074     // private
12075     onBlur : function(){
12076         this.beforeBlur();
12077         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12078             //this.el.removeClass(this.focusClass);
12079         }
12080         this.hasFocus = false;
12081         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12082             this.validate();
12083         }
12084         var v = this.getValue();
12085         if(String(v) !== String(this.startValue)){
12086             this.fireEvent('change', this, v, this.startValue);
12087         }
12088         this.fireEvent("blur", this);
12089     },
12090     
12091     onChange : function(e)
12092     {
12093         var v = this.getValue();
12094         if(String(v) !== String(this.startValue)){
12095             this.fireEvent('change', this, v, this.startValue);
12096         }
12097         
12098     },
12099     
12100     /**
12101      * Resets the current field value to the originally loaded value and clears any validation messages
12102      */
12103     reset : function(){
12104         this.setValue(this.originalValue);
12105         this.validate();
12106     },
12107      /**
12108      * Returns the name of the field
12109      * @return {Mixed} name The name field
12110      */
12111     getName: function(){
12112         return this.name;
12113     },
12114      /**
12115      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12116      * @return {Mixed} value The field value
12117      */
12118     getValue : function(){
12119         
12120         var v = this.inputEl().getValue();
12121         
12122         return v;
12123     },
12124     /**
12125      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12126      * @return {Mixed} value The field value
12127      */
12128     getRawValue : function(){
12129         var v = this.inputEl().getValue();
12130         
12131         return v;
12132     },
12133     
12134     /**
12135      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12136      * @param {Mixed} value The value to set
12137      */
12138     setRawValue : function(v){
12139         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12140     },
12141     
12142     selectText : function(start, end){
12143         var v = this.getRawValue();
12144         if(v.length > 0){
12145             start = start === undefined ? 0 : start;
12146             end = end === undefined ? v.length : end;
12147             var d = this.inputEl().dom;
12148             if(d.setSelectionRange){
12149                 d.setSelectionRange(start, end);
12150             }else if(d.createTextRange){
12151                 var range = d.createTextRange();
12152                 range.moveStart("character", start);
12153                 range.moveEnd("character", v.length-end);
12154                 range.select();
12155             }
12156         }
12157     },
12158     
12159     /**
12160      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12161      * @param {Mixed} value The value to set
12162      */
12163     setValue : function(v){
12164         this.value = v;
12165         if(this.rendered){
12166             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12167             this.validate();
12168         }
12169     },
12170     
12171     /*
12172     processValue : function(value){
12173         if(this.stripCharsRe){
12174             var newValue = value.replace(this.stripCharsRe, '');
12175             if(newValue !== value){
12176                 this.setRawValue(newValue);
12177                 return newValue;
12178             }
12179         }
12180         return value;
12181     },
12182   */
12183     preFocus : function(){
12184         
12185         if(this.selectOnFocus){
12186             this.inputEl().dom.select();
12187         }
12188     },
12189     filterKeys : function(e){
12190         var k = e.getKey();
12191         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12192             return;
12193         }
12194         var c = e.getCharCode(), cc = String.fromCharCode(c);
12195         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12196             return;
12197         }
12198         if(!this.maskRe.test(cc)){
12199             e.stopEvent();
12200         }
12201     },
12202      /**
12203      * Clear any invalid styles/messages for this field
12204      */
12205     clearInvalid : function(){
12206         
12207         if(!this.el || this.preventMark){ // not rendered
12208             return;
12209         }
12210         
12211         
12212         this.el.removeClass([this.invalidClass, 'is-invalid']);
12213         
12214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12215             
12216             var feedback = this.el.select('.form-control-feedback', true).first();
12217             
12218             if(feedback){
12219                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12220             }
12221             
12222         }
12223         
12224         if(this.indicator){
12225             this.indicator.removeClass('visible');
12226             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12227         }
12228         
12229         this.fireEvent('valid', this);
12230     },
12231     
12232      /**
12233      * Mark this field as valid
12234      */
12235     markValid : function()
12236     {
12237         if(!this.el  || this.preventMark){ // not rendered...
12238             return;
12239         }
12240         
12241         this.el.removeClass([this.invalidClass, this.validClass]);
12242         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12243
12244         var feedback = this.el.select('.form-control-feedback', true).first();
12245             
12246         if(feedback){
12247             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12248         }
12249         
12250         if(this.indicator){
12251             this.indicator.removeClass('visible');
12252             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12253         }
12254         
12255         if(this.disabled){
12256             return;
12257         }
12258         
12259            
12260         if(this.allowBlank && !this.getRawValue().length){
12261             return;
12262         }
12263         if (Roo.bootstrap.version == 3) {
12264             this.el.addClass(this.validClass);
12265         } else {
12266             this.inputEl().addClass('is-valid');
12267         }
12268
12269         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12270             
12271             var feedback = this.el.select('.form-control-feedback', true).first();
12272             
12273             if(feedback){
12274                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12275                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12276             }
12277             
12278         }
12279         
12280         this.fireEvent('valid', this);
12281     },
12282     
12283      /**
12284      * Mark this field as invalid
12285      * @param {String} msg The validation message
12286      */
12287     markInvalid : function(msg)
12288     {
12289         if(!this.el  || this.preventMark){ // not rendered
12290             return;
12291         }
12292         
12293         this.el.removeClass([this.invalidClass, this.validClass]);
12294         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12295         
12296         var feedback = this.el.select('.form-control-feedback', true).first();
12297             
12298         if(feedback){
12299             this.el.select('.form-control-feedback', true).first().removeClass(
12300                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12301         }
12302
12303         if(this.disabled){
12304             return;
12305         }
12306         
12307         if(this.allowBlank && !this.getRawValue().length){
12308             return;
12309         }
12310         
12311         if(this.indicator){
12312             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12313             this.indicator.addClass('visible');
12314         }
12315         if (Roo.bootstrap.version == 3) {
12316             this.el.addClass(this.invalidClass);
12317         } else {
12318             this.inputEl().addClass('is-invalid');
12319         }
12320         
12321         
12322         
12323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12324             
12325             var feedback = this.el.select('.form-control-feedback', true).first();
12326             
12327             if(feedback){
12328                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12329                 
12330                 if(this.getValue().length || this.forceFeedback){
12331                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12332                 }
12333                 
12334             }
12335             
12336         }
12337         
12338         this.fireEvent('invalid', this, msg);
12339     },
12340     // private
12341     SafariOnKeyDown : function(event)
12342     {
12343         // this is a workaround for a password hang bug on chrome/ webkit.
12344         if (this.inputEl().dom.type != 'password') {
12345             return;
12346         }
12347         
12348         var isSelectAll = false;
12349         
12350         if(this.inputEl().dom.selectionEnd > 0){
12351             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12352         }
12353         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12354             event.preventDefault();
12355             this.setValue('');
12356             return;
12357         }
12358         
12359         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12360             
12361             event.preventDefault();
12362             // this is very hacky as keydown always get's upper case.
12363             //
12364             var cc = String.fromCharCode(event.getCharCode());
12365             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12366             
12367         }
12368     },
12369     adjustWidth : function(tag, w){
12370         tag = tag.toLowerCase();
12371         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12372             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12373                 if(tag == 'input'){
12374                     return w + 2;
12375                 }
12376                 if(tag == 'textarea'){
12377                     return w-2;
12378                 }
12379             }else if(Roo.isOpera){
12380                 if(tag == 'input'){
12381                     return w + 2;
12382                 }
12383                 if(tag == 'textarea'){
12384                     return w-2;
12385                 }
12386             }
12387         }
12388         return w;
12389     },
12390     
12391     setFieldLabel : function(v)
12392     {
12393         if(!this.rendered){
12394             return;
12395         }
12396         
12397         if(this.indicatorEl()){
12398             var ar = this.el.select('label > span',true);
12399             
12400             if (ar.elements.length) {
12401                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12402                 this.fieldLabel = v;
12403                 return;
12404             }
12405             
12406             var br = this.el.select('label',true);
12407             
12408             if(br.elements.length) {
12409                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12410                 this.fieldLabel = v;
12411                 return;
12412             }
12413             
12414             Roo.log('Cannot Found any of label > span || label in input');
12415             return;
12416         }
12417         
12418         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12419         this.fieldLabel = v;
12420         
12421         
12422     }
12423 });
12424
12425  
12426 /*
12427  * - LGPL
12428  *
12429  * Input
12430  * 
12431  */
12432
12433 /**
12434  * @class Roo.bootstrap.TextArea
12435  * @extends Roo.bootstrap.Input
12436  * Bootstrap TextArea class
12437  * @cfg {Number} cols Specifies the visible width of a text area
12438  * @cfg {Number} rows Specifies the visible number of lines in a text area
12439  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12440  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12441  * @cfg {string} html text
12442  * 
12443  * @constructor
12444  * Create a new TextArea
12445  * @param {Object} config The config object
12446  */
12447
12448 Roo.bootstrap.TextArea = function(config){
12449     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12450    
12451 };
12452
12453 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12454      
12455     cols : false,
12456     rows : 5,
12457     readOnly : false,
12458     warp : 'soft',
12459     resize : false,
12460     value: false,
12461     html: false,
12462     
12463     getAutoCreate : function(){
12464         
12465         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12466         
12467         var id = Roo.id();
12468         
12469         var cfg = {};
12470         
12471         if(this.inputType != 'hidden'){
12472             cfg.cls = 'form-group' //input-group
12473         }
12474         
12475         var input =  {
12476             tag: 'textarea',
12477             id : id,
12478             warp : this.warp,
12479             rows : this.rows,
12480             value : this.value || '',
12481             html: this.html || '',
12482             cls : 'form-control',
12483             placeholder : this.placeholder || '' 
12484             
12485         };
12486         
12487         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12488             input.maxLength = this.maxLength;
12489         }
12490         
12491         if(this.resize){
12492             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12493         }
12494         
12495         if(this.cols){
12496             input.cols = this.cols;
12497         }
12498         
12499         if (this.readOnly) {
12500             input.readonly = true;
12501         }
12502         
12503         if (this.name) {
12504             input.name = this.name;
12505         }
12506         
12507         if (this.size) {
12508             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12509         }
12510         
12511         var settings=this;
12512         ['xs','sm','md','lg'].map(function(size){
12513             if (settings[size]) {
12514                 cfg.cls += ' col-' + size + '-' + settings[size];
12515             }
12516         });
12517         
12518         var inputblock = input;
12519         
12520         if(this.hasFeedback && !this.allowBlank){
12521             
12522             var feedback = {
12523                 tag: 'span',
12524                 cls: 'glyphicon form-control-feedback'
12525             };
12526
12527             inputblock = {
12528                 cls : 'has-feedback',
12529                 cn :  [
12530                     input,
12531                     feedback
12532                 ] 
12533             };  
12534         }
12535         
12536         
12537         if (this.before || this.after) {
12538             
12539             inputblock = {
12540                 cls : 'input-group',
12541                 cn :  [] 
12542             };
12543             if (this.before) {
12544                 inputblock.cn.push({
12545                     tag :'span',
12546                     cls : 'input-group-addon',
12547                     html : this.before
12548                 });
12549             }
12550             
12551             inputblock.cn.push(input);
12552             
12553             if(this.hasFeedback && !this.allowBlank){
12554                 inputblock.cls += ' has-feedback';
12555                 inputblock.cn.push(feedback);
12556             }
12557             
12558             if (this.after) {
12559                 inputblock.cn.push({
12560                     tag :'span',
12561                     cls : 'input-group-addon',
12562                     html : this.after
12563                 });
12564             }
12565             
12566         }
12567         
12568         if (align ==='left' && this.fieldLabel.length) {
12569             cfg.cn = [
12570                 {
12571                     tag: 'label',
12572                     'for' :  id,
12573                     cls : 'control-label',
12574                     html : this.fieldLabel
12575                 },
12576                 {
12577                     cls : "",
12578                     cn: [
12579                         inputblock
12580                     ]
12581                 }
12582
12583             ];
12584             
12585             if(this.labelWidth > 12){
12586                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12587             }
12588
12589             if(this.labelWidth < 13 && this.labelmd == 0){
12590                 this.labelmd = this.labelWidth;
12591             }
12592
12593             if(this.labellg > 0){
12594                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12595                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12596             }
12597
12598             if(this.labelmd > 0){
12599                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12600                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12601             }
12602
12603             if(this.labelsm > 0){
12604                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12605                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12606             }
12607
12608             if(this.labelxs > 0){
12609                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12610                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12611             }
12612             
12613         } else if ( this.fieldLabel.length) {
12614             cfg.cn = [
12615
12616                {
12617                    tag: 'label',
12618                    //cls : 'input-group-addon',
12619                    html : this.fieldLabel
12620
12621                },
12622
12623                inputblock
12624
12625            ];
12626
12627         } else {
12628
12629             cfg.cn = [
12630
12631                 inputblock
12632
12633             ];
12634                 
12635         }
12636         
12637         if (this.disabled) {
12638             input.disabled=true;
12639         }
12640         
12641         return cfg;
12642         
12643     },
12644     /**
12645      * return the real textarea element.
12646      */
12647     inputEl: function ()
12648     {
12649         return this.el.select('textarea.form-control',true).first();
12650     },
12651     
12652     /**
12653      * Clear any invalid styles/messages for this field
12654      */
12655     clearInvalid : function()
12656     {
12657         
12658         if(!this.el || this.preventMark){ // not rendered
12659             return;
12660         }
12661         
12662         var label = this.el.select('label', true).first();
12663         var icon = this.el.select('i.fa-star', true).first();
12664         
12665         if(label && icon){
12666             icon.remove();
12667         }
12668         this.el.removeClass( this.validClass);
12669         this.inputEl().removeClass('is-invalid');
12670          
12671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12672             
12673             var feedback = this.el.select('.form-control-feedback', true).first();
12674             
12675             if(feedback){
12676                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12677             }
12678             
12679         }
12680         
12681         this.fireEvent('valid', this);
12682     },
12683     
12684      /**
12685      * Mark this field as valid
12686      */
12687     markValid : function()
12688     {
12689         if(!this.el  || this.preventMark){ // not rendered
12690             return;
12691         }
12692         
12693         this.el.removeClass([this.invalidClass, this.validClass]);
12694         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12695         
12696         var feedback = this.el.select('.form-control-feedback', true).first();
12697             
12698         if(feedback){
12699             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12700         }
12701
12702         if(this.disabled || this.allowBlank){
12703             return;
12704         }
12705         
12706         var label = this.el.select('label', true).first();
12707         var icon = this.el.select('i.fa-star', true).first();
12708         
12709         if(label && icon){
12710             icon.remove();
12711         }
12712         if (Roo.bootstrap.version == 3) {
12713             this.el.addClass(this.validClass);
12714         } else {
12715             this.inputEl().addClass('is-valid');
12716         }
12717         
12718         
12719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12720             
12721             var feedback = this.el.select('.form-control-feedback', true).first();
12722             
12723             if(feedback){
12724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12725                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12726             }
12727             
12728         }
12729         
12730         this.fireEvent('valid', this);
12731     },
12732     
12733      /**
12734      * Mark this field as invalid
12735      * @param {String} msg The validation message
12736      */
12737     markInvalid : function(msg)
12738     {
12739         if(!this.el  || this.preventMark){ // not rendered
12740             return;
12741         }
12742         
12743         this.el.removeClass([this.invalidClass, this.validClass]);
12744         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12745         
12746         var feedback = this.el.select('.form-control-feedback', true).first();
12747             
12748         if(feedback){
12749             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12750         }
12751
12752         if(this.disabled || this.allowBlank){
12753             return;
12754         }
12755         
12756         var label = this.el.select('label', true).first();
12757         var icon = this.el.select('i.fa-star', true).first();
12758         
12759         if(!this.getValue().length && label && !icon){
12760             this.el.createChild({
12761                 tag : 'i',
12762                 cls : 'text-danger fa fa-lg fa-star',
12763                 tooltip : 'This field is required',
12764                 style : 'margin-right:5px;'
12765             }, label, true);
12766         }
12767         
12768         if (Roo.bootstrap.version == 3) {
12769             this.el.addClass(this.invalidClass);
12770         } else {
12771             this.inputEl().addClass('is-invalid');
12772         }
12773         
12774         // fixme ... this may be depricated need to test..
12775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12776             
12777             var feedback = this.el.select('.form-control-feedback', true).first();
12778             
12779             if(feedback){
12780                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12781                 
12782                 if(this.getValue().length || this.forceFeedback){
12783                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12784                 }
12785                 
12786             }
12787             
12788         }
12789         
12790         this.fireEvent('invalid', this, msg);
12791     }
12792 });
12793
12794  
12795 /*
12796  * - LGPL
12797  *
12798  * trigger field - base class for combo..
12799  * 
12800  */
12801  
12802 /**
12803  * @class Roo.bootstrap.TriggerField
12804  * @extends Roo.bootstrap.Input
12805  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12806  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12807  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12808  * for which you can provide a custom implementation.  For example:
12809  * <pre><code>
12810 var trigger = new Roo.bootstrap.TriggerField();
12811 trigger.onTriggerClick = myTriggerFn;
12812 trigger.applyTo('my-field');
12813 </code></pre>
12814  *
12815  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12816  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12817  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12818  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12819  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12820
12821  * @constructor
12822  * Create a new TriggerField.
12823  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12824  * to the base TextField)
12825  */
12826 Roo.bootstrap.TriggerField = function(config){
12827     this.mimicing = false;
12828     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12829 };
12830
12831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12832     /**
12833      * @cfg {String} triggerClass A CSS class to apply to the trigger
12834      */
12835      /**
12836      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12837      */
12838     hideTrigger:false,
12839
12840     /**
12841      * @cfg {Boolean} removable (true|false) special filter default false
12842      */
12843     removable : false,
12844     
12845     /** @cfg {Boolean} grow @hide */
12846     /** @cfg {Number} growMin @hide */
12847     /** @cfg {Number} growMax @hide */
12848
12849     /**
12850      * @hide 
12851      * @method
12852      */
12853     autoSize: Roo.emptyFn,
12854     // private
12855     monitorTab : true,
12856     // private
12857     deferHeight : true,
12858
12859     
12860     actionMode : 'wrap',
12861     
12862     caret : false,
12863     
12864     
12865     getAutoCreate : function(){
12866        
12867         var align = this.labelAlign || this.parentLabelAlign();
12868         
12869         var id = Roo.id();
12870         
12871         var cfg = {
12872             cls: 'form-group' //input-group
12873         };
12874         
12875         
12876         var input =  {
12877             tag: 'input',
12878             id : id,
12879             type : this.inputType,
12880             cls : 'form-control',
12881             autocomplete: 'new-password',
12882             placeholder : this.placeholder || '' 
12883             
12884         };
12885         if (this.name) {
12886             input.name = this.name;
12887         }
12888         if (this.size) {
12889             input.cls += ' input-' + this.size;
12890         }
12891         
12892         if (this.disabled) {
12893             input.disabled=true;
12894         }
12895         
12896         var inputblock = input;
12897         
12898         if(this.hasFeedback && !this.allowBlank){
12899             
12900             var feedback = {
12901                 tag: 'span',
12902                 cls: 'glyphicon form-control-feedback'
12903             };
12904             
12905             if(this.removable && !this.editable  ){
12906                 inputblock = {
12907                     cls : 'has-feedback',
12908                     cn :  [
12909                         inputblock,
12910                         {
12911                             tag: 'button',
12912                             html : 'x',
12913                             cls : 'roo-combo-removable-btn close'
12914                         },
12915                         feedback
12916                     ] 
12917                 };
12918             } else {
12919                 inputblock = {
12920                     cls : 'has-feedback',
12921                     cn :  [
12922                         inputblock,
12923                         feedback
12924                     ] 
12925                 };
12926             }
12927
12928         } else {
12929             if(this.removable && !this.editable ){
12930                 inputblock = {
12931                     cls : 'roo-removable',
12932                     cn :  [
12933                         inputblock,
12934                         {
12935                             tag: 'button',
12936                             html : 'x',
12937                             cls : 'roo-combo-removable-btn close'
12938                         }
12939                     ] 
12940                 };
12941             }
12942         }
12943         
12944         if (this.before || this.after) {
12945             
12946             inputblock = {
12947                 cls : 'input-group',
12948                 cn :  [] 
12949             };
12950             if (this.before) {
12951                 inputblock.cn.push({
12952                     tag :'span',
12953                     cls : 'input-group-addon input-group-prepend input-group-text',
12954                     html : this.before
12955                 });
12956             }
12957             
12958             inputblock.cn.push(input);
12959             
12960             if(this.hasFeedback && !this.allowBlank){
12961                 inputblock.cls += ' has-feedback';
12962                 inputblock.cn.push(feedback);
12963             }
12964             
12965             if (this.after) {
12966                 inputblock.cn.push({
12967                     tag :'span',
12968                     cls : 'input-group-addon input-group-append input-group-text',
12969                     html : this.after
12970                 });
12971             }
12972             
12973         };
12974         
12975       
12976         
12977         var ibwrap = inputblock;
12978         
12979         if(this.multiple){
12980             ibwrap = {
12981                 tag: 'ul',
12982                 cls: 'roo-select2-choices',
12983                 cn:[
12984                     {
12985                         tag: 'li',
12986                         cls: 'roo-select2-search-field',
12987                         cn: [
12988
12989                             inputblock
12990                         ]
12991                     }
12992                 ]
12993             };
12994                 
12995         }
12996         
12997         var combobox = {
12998             cls: 'roo-select2-container input-group',
12999             cn: [
13000                  {
13001                     tag: 'input',
13002                     type : 'hidden',
13003                     cls: 'form-hidden-field'
13004                 },
13005                 ibwrap
13006             ]
13007         };
13008         
13009         if(!this.multiple && this.showToggleBtn){
13010             
13011             var caret = {
13012                         tag: 'span',
13013                         cls: 'caret'
13014              };
13015             if (this.caret != false) {
13016                 caret = {
13017                      tag: 'i',
13018                      cls: 'fa fa-' + this.caret
13019                 };
13020                 
13021             }
13022             
13023             combobox.cn.push({
13024                 tag :'span',
13025                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13026                 cn : [
13027                     Roo.bootstrap.version == 3 ? caret : '',
13028                     {
13029                         tag: 'span',
13030                         cls: 'combobox-clear',
13031                         cn  : [
13032                             {
13033                                 tag : 'i',
13034                                 cls: 'icon-remove'
13035                             }
13036                         ]
13037                     }
13038                 ]
13039
13040             })
13041         }
13042         
13043         if(this.multiple){
13044             combobox.cls += ' roo-select2-container-multi';
13045         }
13046          var indicator = {
13047             tag : 'i',
13048             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13049             tooltip : 'This field is required'
13050         };
13051         if (Roo.bootstrap.version == 4) {
13052             indicator = {
13053                 tag : 'i',
13054                 style : 'display:none'
13055             };
13056         }
13057         
13058         
13059         if (align ==='left' && this.fieldLabel.length) {
13060             
13061             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13062
13063             cfg.cn = [
13064                 indicator,
13065                 {
13066                     tag: 'label',
13067                     'for' :  id,
13068                     cls : 'control-label',
13069                     html : this.fieldLabel
13070
13071                 },
13072                 {
13073                     cls : "", 
13074                     cn: [
13075                         combobox
13076                     ]
13077                 }
13078
13079             ];
13080             
13081             var labelCfg = cfg.cn[1];
13082             var contentCfg = cfg.cn[2];
13083             
13084             if(this.indicatorpos == 'right'){
13085                 cfg.cn = [
13086                     {
13087                         tag: 'label',
13088                         'for' :  id,
13089                         cls : 'control-label',
13090                         cn : [
13091                             {
13092                                 tag : 'span',
13093                                 html : this.fieldLabel
13094                             },
13095                             indicator
13096                         ]
13097                     },
13098                     {
13099                         cls : "", 
13100                         cn: [
13101                             combobox
13102                         ]
13103                     }
13104
13105                 ];
13106                 
13107                 labelCfg = cfg.cn[0];
13108                 contentCfg = cfg.cn[1];
13109             }
13110             
13111             if(this.labelWidth > 12){
13112                 labelCfg.style = "width: " + this.labelWidth + 'px';
13113             }
13114             
13115             if(this.labelWidth < 13 && this.labelmd == 0){
13116                 this.labelmd = this.labelWidth;
13117             }
13118             
13119             if(this.labellg > 0){
13120                 labelCfg.cls += ' col-lg-' + this.labellg;
13121                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13122             }
13123             
13124             if(this.labelmd > 0){
13125                 labelCfg.cls += ' col-md-' + this.labelmd;
13126                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13127             }
13128             
13129             if(this.labelsm > 0){
13130                 labelCfg.cls += ' col-sm-' + this.labelsm;
13131                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13132             }
13133             
13134             if(this.labelxs > 0){
13135                 labelCfg.cls += ' col-xs-' + this.labelxs;
13136                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13137             }
13138             
13139         } else if ( this.fieldLabel.length) {
13140 //                Roo.log(" label");
13141             cfg.cn = [
13142                 indicator,
13143                {
13144                    tag: 'label',
13145                    //cls : 'input-group-addon',
13146                    html : this.fieldLabel
13147
13148                },
13149
13150                combobox
13151
13152             ];
13153             
13154             if(this.indicatorpos == 'right'){
13155                 
13156                 cfg.cn = [
13157                     {
13158                        tag: 'label',
13159                        cn : [
13160                            {
13161                                tag : 'span',
13162                                html : this.fieldLabel
13163                            },
13164                            indicator
13165                        ]
13166
13167                     },
13168                     combobox
13169
13170                 ];
13171
13172             }
13173
13174         } else {
13175             
13176 //                Roo.log(" no label && no align");
13177                 cfg = combobox
13178                      
13179                 
13180         }
13181         
13182         var settings=this;
13183         ['xs','sm','md','lg'].map(function(size){
13184             if (settings[size]) {
13185                 cfg.cls += ' col-' + size + '-' + settings[size];
13186             }
13187         });
13188         
13189         return cfg;
13190         
13191     },
13192     
13193     
13194     
13195     // private
13196     onResize : function(w, h){
13197 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13198 //        if(typeof w == 'number'){
13199 //            var x = w - this.trigger.getWidth();
13200 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13201 //            this.trigger.setStyle('left', x+'px');
13202 //        }
13203     },
13204
13205     // private
13206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13207
13208     // private
13209     getResizeEl : function(){
13210         return this.inputEl();
13211     },
13212
13213     // private
13214     getPositionEl : function(){
13215         return this.inputEl();
13216     },
13217
13218     // private
13219     alignErrorIcon : function(){
13220         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13221     },
13222
13223     // private
13224     initEvents : function(){
13225         
13226         this.createList();
13227         
13228         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13229         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13230         if(!this.multiple && this.showToggleBtn){
13231             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13232             if(this.hideTrigger){
13233                 this.trigger.setDisplayed(false);
13234             }
13235             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13236         }
13237         
13238         if(this.multiple){
13239             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13240         }
13241         
13242         if(this.removable && !this.editable && !this.tickable){
13243             var close = this.closeTriggerEl();
13244             
13245             if(close){
13246                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13247                 close.on('click', this.removeBtnClick, this, close);
13248             }
13249         }
13250         
13251         //this.trigger.addClassOnOver('x-form-trigger-over');
13252         //this.trigger.addClassOnClick('x-form-trigger-click');
13253         
13254         //if(!this.width){
13255         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13256         //}
13257     },
13258     
13259     closeTriggerEl : function()
13260     {
13261         var close = this.el.select('.roo-combo-removable-btn', true).first();
13262         return close ? close : false;
13263     },
13264     
13265     removeBtnClick : function(e, h, el)
13266     {
13267         e.preventDefault();
13268         
13269         if(this.fireEvent("remove", this) !== false){
13270             this.reset();
13271             this.fireEvent("afterremove", this)
13272         }
13273     },
13274     
13275     createList : function()
13276     {
13277         this.list = Roo.get(document.body).createChild({
13278             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13279             cls: 'typeahead typeahead-long dropdown-menu shadow',
13280             style: 'display:none'
13281         });
13282         
13283         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13284         
13285     },
13286
13287     // private
13288     initTrigger : function(){
13289        
13290     },
13291
13292     // private
13293     onDestroy : function(){
13294         if(this.trigger){
13295             this.trigger.removeAllListeners();
13296           //  this.trigger.remove();
13297         }
13298         //if(this.wrap){
13299         //    this.wrap.remove();
13300         //}
13301         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13302     },
13303
13304     // private
13305     onFocus : function(){
13306         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13307         /*
13308         if(!this.mimicing){
13309             this.wrap.addClass('x-trigger-wrap-focus');
13310             this.mimicing = true;
13311             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13312             if(this.monitorTab){
13313                 this.el.on("keydown", this.checkTab, this);
13314             }
13315         }
13316         */
13317     },
13318
13319     // private
13320     checkTab : function(e){
13321         if(e.getKey() == e.TAB){
13322             this.triggerBlur();
13323         }
13324     },
13325
13326     // private
13327     onBlur : function(){
13328         // do nothing
13329     },
13330
13331     // private
13332     mimicBlur : function(e, t){
13333         /*
13334         if(!this.wrap.contains(t) && this.validateBlur()){
13335             this.triggerBlur();
13336         }
13337         */
13338     },
13339
13340     // private
13341     triggerBlur : function(){
13342         this.mimicing = false;
13343         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13344         if(this.monitorTab){
13345             this.el.un("keydown", this.checkTab, this);
13346         }
13347         //this.wrap.removeClass('x-trigger-wrap-focus');
13348         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13349     },
13350
13351     // private
13352     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13353     validateBlur : function(e, t){
13354         return true;
13355     },
13356
13357     // private
13358     onDisable : function(){
13359         this.inputEl().dom.disabled = true;
13360         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13361         //if(this.wrap){
13362         //    this.wrap.addClass('x-item-disabled');
13363         //}
13364     },
13365
13366     // private
13367     onEnable : function(){
13368         this.inputEl().dom.disabled = false;
13369         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13370         //if(this.wrap){
13371         //    this.el.removeClass('x-item-disabled');
13372         //}
13373     },
13374
13375     // private
13376     onShow : function(){
13377         var ae = this.getActionEl();
13378         
13379         if(ae){
13380             ae.dom.style.display = '';
13381             ae.dom.style.visibility = 'visible';
13382         }
13383     },
13384
13385     // private
13386     
13387     onHide : function(){
13388         var ae = this.getActionEl();
13389         ae.dom.style.display = 'none';
13390     },
13391
13392     /**
13393      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13394      * by an implementing function.
13395      * @method
13396      * @param {EventObject} e
13397      */
13398     onTriggerClick : Roo.emptyFn
13399 });
13400  
13401 /*
13402 * Licence: LGPL
13403 */
13404
13405 /**
13406  * @class Roo.bootstrap.CardUploader
13407  * @extends Roo.bootstrap.Button
13408  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13409  * @cfg {Number} errorTimeout default 3000
13410  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13411  * @cfg {Array}  html The button text.
13412
13413  *
13414  * @constructor
13415  * Create a new CardUploader
13416  * @param {Object} config The config object
13417  */
13418
13419 Roo.bootstrap.CardUploader = function(config){
13420     
13421  
13422     
13423     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13424     
13425     
13426     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13427         return r.data.id
13428      });
13429     
13430      this.addEvents({
13431          // raw events
13432         /**
13433          * @event preview
13434          * When a image is clicked on - and needs to display a slideshow or similar..
13435          * @param {Roo.bootstrap.Card} this
13436          * @param {Object} The image information data 
13437          *
13438          */
13439         'preview' : true,
13440          /**
13441          * @event download
13442          * When a the download link is clicked
13443          * @param {Roo.bootstrap.Card} this
13444          * @param {Object} The image information data  contains 
13445          */
13446         'download' : true
13447         
13448     });
13449 };
13450  
13451 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13452     
13453      
13454     errorTimeout : 3000,
13455      
13456     images : false,
13457    
13458     fileCollection : false,
13459     allowBlank : true,
13460     
13461     getAutoCreate : function()
13462     {
13463         
13464         var cfg =  {
13465             cls :'form-group' ,
13466             cn : [
13467                
13468                 {
13469                     tag: 'label',
13470                    //cls : 'input-group-addon',
13471                     html : this.fieldLabel
13472
13473                 },
13474
13475                 {
13476                     tag: 'input',
13477                     type : 'hidden',
13478                     name : this.name,
13479                     value : this.value,
13480                     cls : 'd-none  form-control'
13481                 },
13482                 
13483                 {
13484                     tag: 'input',
13485                     multiple : 'multiple',
13486                     type : 'file',
13487                     cls : 'd-none  roo-card-upload-selector'
13488                 },
13489                 
13490                 {
13491                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13492                 },
13493                 {
13494                     cls : 'card-columns roo-card-uploader-container'
13495                 }
13496
13497             ]
13498         };
13499            
13500          
13501         return cfg;
13502     },
13503     
13504     getChildContainer : function() /// what children are added to.
13505     {
13506         return this.containerEl;
13507     },
13508    
13509     getButtonContainer : function() /// what children are added to.
13510     {
13511         return this.el.select(".roo-card-uploader-button-container").first();
13512     },
13513    
13514     initEvents : function()
13515     {
13516         
13517         Roo.bootstrap.Input.prototype.initEvents.call(this);
13518         
13519         var t = this;
13520         this.addxtype({
13521             xns: Roo.bootstrap,
13522
13523             xtype : 'Button',
13524             container_method : 'getButtonContainer' ,            
13525             html :  this.html, // fix changable?
13526             cls : 'w-100 ',
13527             listeners : {
13528                 'click' : function(btn, e) {
13529                     t.onClick(e);
13530                 }
13531             }
13532         });
13533         
13534         
13535         
13536         
13537         this.urlAPI = (window.createObjectURL && window) || 
13538                                 (window.URL && URL.revokeObjectURL && URL) || 
13539                                 (window.webkitURL && webkitURL);
13540                         
13541          
13542          
13543          
13544         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13545         
13546         this.selectorEl.on('change', this.onFileSelected, this);
13547         if (this.images) {
13548             var t = this;
13549             this.images.forEach(function(img) {
13550                 t.addCard(img)
13551             });
13552             this.images = false;
13553         }
13554         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13555          
13556        
13557     },
13558     
13559    
13560     onClick : function(e)
13561     {
13562         e.preventDefault();
13563          
13564         this.selectorEl.dom.click();
13565          
13566     },
13567     
13568     onFileSelected : function(e)
13569     {
13570         e.preventDefault();
13571         
13572         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13573             return;
13574         }
13575         
13576         Roo.each(this.selectorEl.dom.files, function(file){    
13577             this.addFile(file);
13578         }, this);
13579          
13580     },
13581     
13582       
13583     
13584       
13585     
13586     addFile : function(file)
13587     {
13588            
13589         if(typeof(file) === 'string'){
13590             throw "Add file by name?"; // should not happen
13591             return;
13592         }
13593         
13594         if(!file || !this.urlAPI){
13595             return;
13596         }
13597         
13598         // file;
13599         // file.type;
13600         
13601         var _this = this;
13602         
13603         
13604         var url = _this.urlAPI.createObjectURL( file);
13605            
13606         this.addCard({
13607             id : Roo.bootstrap.CardUploader.ID--,
13608             is_uploaded : false,
13609             src : url,
13610             srcfile : file,
13611             title : file.name,
13612             mimetype : file.type,
13613             preview : false,
13614             is_deleted : 0
13615         });
13616         
13617     },
13618     
13619     /**
13620      * addCard - add an Attachment to the uploader
13621      * @param data - the data about the image to upload
13622      *
13623      * {
13624           id : 123
13625           title : "Title of file",
13626           is_uploaded : false,
13627           src : "http://.....",
13628           srcfile : { the File upload object },
13629           mimetype : file.type,
13630           preview : false,
13631           is_deleted : 0
13632           .. any other data...
13633         }
13634      *
13635      * 
13636     */
13637     
13638     addCard : function (data)
13639     {
13640         // hidden input element?
13641         // if the file is not an image...
13642         //then we need to use something other that and header_image
13643         var t = this;
13644         //   remove.....
13645         var footer = [
13646             {
13647                 xns : Roo.bootstrap,
13648                 xtype : 'CardFooter',
13649                  items: [
13650                     {
13651                         xns : Roo.bootstrap,
13652                         xtype : 'Element',
13653                         cls : 'd-flex',
13654                         items : [
13655                             
13656                             {
13657                                 xns : Roo.bootstrap,
13658                                 xtype : 'Button',
13659                                 html : String.format("<small>{0}</small>", data.title),
13660                                 cls : 'col-10 text-left',
13661                                 size: 'sm',
13662                                 weight: 'link',
13663                                 fa : 'download',
13664                                 listeners : {
13665                                     click : function() {
13666                                      
13667                                         t.fireEvent( "download", t, data );
13668                                     }
13669                                 }
13670                             },
13671                           
13672                             {
13673                                 xns : Roo.bootstrap,
13674                                 xtype : 'Button',
13675                                 style: 'max-height: 28px; ',
13676                                 size : 'sm',
13677                                 weight: 'danger',
13678                                 cls : 'col-2',
13679                                 fa : 'times',
13680                                 listeners : {
13681                                     click : function() {
13682                                         t.removeCard(data.id)
13683                                     }
13684                                 }
13685                             }
13686                         ]
13687                     }
13688                     
13689                 ] 
13690             }
13691             
13692         ];
13693         
13694         var cn = this.addxtype(
13695             {
13696                  
13697                 xns : Roo.bootstrap,
13698                 xtype : 'Card',
13699                 closeable : true,
13700                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13701                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13702                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13703                 data : data,
13704                 html : false,
13705                  
13706                 items : footer,
13707                 initEvents : function() {
13708                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13709                     var card = this;
13710                     this.imgEl = this.el.select('.card-img-top').first();
13711                     if (this.imgEl) {
13712                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13713                         this.imgEl.set({ 'pointer' : 'cursor' });
13714                                   
13715                     }
13716                     this.getCardFooter().addClass('p-1');
13717                     
13718                   
13719                 }
13720                 
13721             }
13722         );
13723         // dont' really need ot update items.
13724         // this.items.push(cn);
13725         this.fileCollection.add(cn);
13726         
13727         if (!data.srcfile) {
13728             this.updateInput();
13729             return;
13730         }
13731             
13732         var _t = this;
13733         var reader = new FileReader();
13734         reader.addEventListener("load", function() {  
13735             data.srcdata =  reader.result;
13736             _t.updateInput();
13737         });
13738         reader.readAsDataURL(data.srcfile);
13739         
13740         
13741         
13742     },
13743     removeCard : function(id)
13744     {
13745         
13746         var card  = this.fileCollection.get(id);
13747         card.data.is_deleted = 1;
13748         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13749         //this.fileCollection.remove(card);
13750         //this.items = this.items.filter(function(e) { return e != card });
13751         // dont' really need ot update items.
13752         card.el.dom.parentNode.removeChild(card.el.dom);
13753         this.updateInput();
13754
13755         
13756     },
13757     reset: function()
13758     {
13759         this.fileCollection.each(function(card) {
13760             if (card.el.dom && card.el.dom.parentNode) {
13761                 card.el.dom.parentNode.removeChild(card.el.dom);
13762             }
13763         });
13764         this.fileCollection.clear();
13765         this.updateInput();
13766     },
13767     
13768     updateInput : function()
13769     {
13770          var data = [];
13771         this.fileCollection.each(function(e) {
13772             data.push(e.data);
13773             
13774         });
13775         this.inputEl().dom.value = JSON.stringify(data);
13776         
13777         
13778         
13779     }
13780     
13781     
13782 });
13783
13784
13785 Roo.bootstrap.CardUploader.ID = -1;/*
13786  * Based on:
13787  * Ext JS Library 1.1.1
13788  * Copyright(c) 2006-2007, Ext JS, LLC.
13789  *
13790  * Originally Released Under LGPL - original licence link has changed is not relivant.
13791  *
13792  * Fork - LGPL
13793  * <script type="text/javascript">
13794  */
13795
13796
13797 /**
13798  * @class Roo.data.SortTypes
13799  * @singleton
13800  * Defines the default sorting (casting?) comparison functions used when sorting data.
13801  */
13802 Roo.data.SortTypes = {
13803     /**
13804      * Default sort that does nothing
13805      * @param {Mixed} s The value being converted
13806      * @return {Mixed} The comparison value
13807      */
13808     none : function(s){
13809         return s;
13810     },
13811     
13812     /**
13813      * The regular expression used to strip tags
13814      * @type {RegExp}
13815      * @property
13816      */
13817     stripTagsRE : /<\/?[^>]+>/gi,
13818     
13819     /**
13820      * Strips all HTML tags to sort on text only
13821      * @param {Mixed} s The value being converted
13822      * @return {String} The comparison value
13823      */
13824     asText : function(s){
13825         return String(s).replace(this.stripTagsRE, "");
13826     },
13827     
13828     /**
13829      * Strips all HTML tags to sort on text only - Case insensitive
13830      * @param {Mixed} s The value being converted
13831      * @return {String} The comparison value
13832      */
13833     asUCText : function(s){
13834         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13835     },
13836     
13837     /**
13838      * Case insensitive string
13839      * @param {Mixed} s The value being converted
13840      * @return {String} The comparison value
13841      */
13842     asUCString : function(s) {
13843         return String(s).toUpperCase();
13844     },
13845     
13846     /**
13847      * Date sorting
13848      * @param {Mixed} s The value being converted
13849      * @return {Number} The comparison value
13850      */
13851     asDate : function(s) {
13852         if(!s){
13853             return 0;
13854         }
13855         if(s instanceof Date){
13856             return s.getTime();
13857         }
13858         return Date.parse(String(s));
13859     },
13860     
13861     /**
13862      * Float sorting
13863      * @param {Mixed} s The value being converted
13864      * @return {Float} The comparison value
13865      */
13866     asFloat : function(s) {
13867         var val = parseFloat(String(s).replace(/,/g, ""));
13868         if(isNaN(val)) {
13869             val = 0;
13870         }
13871         return val;
13872     },
13873     
13874     /**
13875      * Integer sorting
13876      * @param {Mixed} s The value being converted
13877      * @return {Number} The comparison value
13878      */
13879     asInt : function(s) {
13880         var val = parseInt(String(s).replace(/,/g, ""));
13881         if(isNaN(val)) {
13882             val = 0;
13883         }
13884         return val;
13885     }
13886 };/*
13887  * Based on:
13888  * Ext JS Library 1.1.1
13889  * Copyright(c) 2006-2007, Ext JS, LLC.
13890  *
13891  * Originally Released Under LGPL - original licence link has changed is not relivant.
13892  *
13893  * Fork - LGPL
13894  * <script type="text/javascript">
13895  */
13896
13897 /**
13898 * @class Roo.data.Record
13899  * Instances of this class encapsulate both record <em>definition</em> information, and record
13900  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13901  * to access Records cached in an {@link Roo.data.Store} object.<br>
13902  * <p>
13903  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13904  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13905  * objects.<br>
13906  * <p>
13907  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13908  * @constructor
13909  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13910  * {@link #create}. The parameters are the same.
13911  * @param {Array} data An associative Array of data values keyed by the field name.
13912  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13913  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13914  * not specified an integer id is generated.
13915  */
13916 Roo.data.Record = function(data, id){
13917     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13918     this.data = data;
13919 };
13920
13921 /**
13922  * Generate a constructor for a specific record layout.
13923  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13924  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13925  * Each field definition object may contain the following properties: <ul>
13926  * <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,
13927  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13928  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13929  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13930  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13931  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13932  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13933  * this may be omitted.</p></li>
13934  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13935  * <ul><li>auto (Default, implies no conversion)</li>
13936  * <li>string</li>
13937  * <li>int</li>
13938  * <li>float</li>
13939  * <li>boolean</li>
13940  * <li>date</li></ul></p></li>
13941  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13942  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13943  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13944  * by the Reader into an object that will be stored in the Record. It is passed the
13945  * following parameters:<ul>
13946  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13947  * </ul></p></li>
13948  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13949  * </ul>
13950  * <br>usage:<br><pre><code>
13951 var TopicRecord = Roo.data.Record.create(
13952     {name: 'title', mapping: 'topic_title'},
13953     {name: 'author', mapping: 'username'},
13954     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13955     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13956     {name: 'lastPoster', mapping: 'user2'},
13957     {name: 'excerpt', mapping: 'post_text'}
13958 );
13959
13960 var myNewRecord = new TopicRecord({
13961     title: 'Do my job please',
13962     author: 'noobie',
13963     totalPosts: 1,
13964     lastPost: new Date(),
13965     lastPoster: 'Animal',
13966     excerpt: 'No way dude!'
13967 });
13968 myStore.add(myNewRecord);
13969 </code></pre>
13970  * @method create
13971  * @static
13972  */
13973 Roo.data.Record.create = function(o){
13974     var f = function(){
13975         f.superclass.constructor.apply(this, arguments);
13976     };
13977     Roo.extend(f, Roo.data.Record);
13978     var p = f.prototype;
13979     p.fields = new Roo.util.MixedCollection(false, function(field){
13980         return field.name;
13981     });
13982     for(var i = 0, len = o.length; i < len; i++){
13983         p.fields.add(new Roo.data.Field(o[i]));
13984     }
13985     f.getField = function(name){
13986         return p.fields.get(name);  
13987     };
13988     return f;
13989 };
13990
13991 Roo.data.Record.AUTO_ID = 1000;
13992 Roo.data.Record.EDIT = 'edit';
13993 Roo.data.Record.REJECT = 'reject';
13994 Roo.data.Record.COMMIT = 'commit';
13995
13996 Roo.data.Record.prototype = {
13997     /**
13998      * Readonly flag - true if this record has been modified.
13999      * @type Boolean
14000      */
14001     dirty : false,
14002     editing : false,
14003     error: null,
14004     modified: null,
14005
14006     // private
14007     join : function(store){
14008         this.store = store;
14009     },
14010
14011     /**
14012      * Set the named field to the specified value.
14013      * @param {String} name The name of the field to set.
14014      * @param {Object} value The value to set the field to.
14015      */
14016     set : function(name, value){
14017         if(this.data[name] == value){
14018             return;
14019         }
14020         this.dirty = true;
14021         if(!this.modified){
14022             this.modified = {};
14023         }
14024         if(typeof this.modified[name] == 'undefined'){
14025             this.modified[name] = this.data[name];
14026         }
14027         this.data[name] = value;
14028         if(!this.editing && this.store){
14029             this.store.afterEdit(this);
14030         }       
14031     },
14032
14033     /**
14034      * Get the value of the named field.
14035      * @param {String} name The name of the field to get the value of.
14036      * @return {Object} The value of the field.
14037      */
14038     get : function(name){
14039         return this.data[name]; 
14040     },
14041
14042     // private
14043     beginEdit : function(){
14044         this.editing = true;
14045         this.modified = {}; 
14046     },
14047
14048     // private
14049     cancelEdit : function(){
14050         this.editing = false;
14051         delete this.modified;
14052     },
14053
14054     // private
14055     endEdit : function(){
14056         this.editing = false;
14057         if(this.dirty && this.store){
14058             this.store.afterEdit(this);
14059         }
14060     },
14061
14062     /**
14063      * Usually called by the {@link Roo.data.Store} which owns the Record.
14064      * Rejects all changes made to the Record since either creation, or the last commit operation.
14065      * Modified fields are reverted to their original values.
14066      * <p>
14067      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14068      * of reject operations.
14069      */
14070     reject : function(){
14071         var m = this.modified;
14072         for(var n in m){
14073             if(typeof m[n] != "function"){
14074                 this.data[n] = m[n];
14075             }
14076         }
14077         this.dirty = false;
14078         delete this.modified;
14079         this.editing = false;
14080         if(this.store){
14081             this.store.afterReject(this);
14082         }
14083     },
14084
14085     /**
14086      * Usually called by the {@link Roo.data.Store} which owns the Record.
14087      * Commits all changes made to the Record since either creation, or the last commit operation.
14088      * <p>
14089      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14090      * of commit operations.
14091      */
14092     commit : function(){
14093         this.dirty = false;
14094         delete this.modified;
14095         this.editing = false;
14096         if(this.store){
14097             this.store.afterCommit(this);
14098         }
14099     },
14100
14101     // private
14102     hasError : function(){
14103         return this.error != null;
14104     },
14105
14106     // private
14107     clearError : function(){
14108         this.error = null;
14109     },
14110
14111     /**
14112      * Creates a copy of this record.
14113      * @param {String} id (optional) A new record id if you don't want to use this record's id
14114      * @return {Record}
14115      */
14116     copy : function(newId) {
14117         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14118     }
14119 };/*
14120  * Based on:
14121  * Ext JS Library 1.1.1
14122  * Copyright(c) 2006-2007, Ext JS, LLC.
14123  *
14124  * Originally Released Under LGPL - original licence link has changed is not relivant.
14125  *
14126  * Fork - LGPL
14127  * <script type="text/javascript">
14128  */
14129
14130
14131
14132 /**
14133  * @class Roo.data.Store
14134  * @extends Roo.util.Observable
14135  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14136  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14137  * <p>
14138  * 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
14139  * has no knowledge of the format of the data returned by the Proxy.<br>
14140  * <p>
14141  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14142  * instances from the data object. These records are cached and made available through accessor functions.
14143  * @constructor
14144  * Creates a new Store.
14145  * @param {Object} config A config object containing the objects needed for the Store to access data,
14146  * and read the data into Records.
14147  */
14148 Roo.data.Store = function(config){
14149     this.data = new Roo.util.MixedCollection(false);
14150     this.data.getKey = function(o){
14151         return o.id;
14152     };
14153     this.baseParams = {};
14154     // private
14155     this.paramNames = {
14156         "start" : "start",
14157         "limit" : "limit",
14158         "sort" : "sort",
14159         "dir" : "dir",
14160         "multisort" : "_multisort"
14161     };
14162
14163     if(config && config.data){
14164         this.inlineData = config.data;
14165         delete config.data;
14166     }
14167
14168     Roo.apply(this, config);
14169     
14170     if(this.reader){ // reader passed
14171         this.reader = Roo.factory(this.reader, Roo.data);
14172         this.reader.xmodule = this.xmodule || false;
14173         if(!this.recordType){
14174             this.recordType = this.reader.recordType;
14175         }
14176         if(this.reader.onMetaChange){
14177             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14178         }
14179     }
14180
14181     if(this.recordType){
14182         this.fields = this.recordType.prototype.fields;
14183     }
14184     this.modified = [];
14185
14186     this.addEvents({
14187         /**
14188          * @event datachanged
14189          * Fires when the data cache has changed, and a widget which is using this Store
14190          * as a Record cache should refresh its view.
14191          * @param {Store} this
14192          */
14193         datachanged : true,
14194         /**
14195          * @event metachange
14196          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14197          * @param {Store} this
14198          * @param {Object} meta The JSON metadata
14199          */
14200         metachange : true,
14201         /**
14202          * @event add
14203          * Fires when Records have been added to the Store
14204          * @param {Store} this
14205          * @param {Roo.data.Record[]} records The array of Records added
14206          * @param {Number} index The index at which the record(s) were added
14207          */
14208         add : true,
14209         /**
14210          * @event remove
14211          * Fires when a Record has been removed from the Store
14212          * @param {Store} this
14213          * @param {Roo.data.Record} record The Record that was removed
14214          * @param {Number} index The index at which the record was removed
14215          */
14216         remove : true,
14217         /**
14218          * @event update
14219          * Fires when a Record has been updated
14220          * @param {Store} this
14221          * @param {Roo.data.Record} record The Record that was updated
14222          * @param {String} operation The update operation being performed.  Value may be one of:
14223          * <pre><code>
14224  Roo.data.Record.EDIT
14225  Roo.data.Record.REJECT
14226  Roo.data.Record.COMMIT
14227          * </code></pre>
14228          */
14229         update : true,
14230         /**
14231          * @event clear
14232          * Fires when the data cache has been cleared.
14233          * @param {Store} this
14234          */
14235         clear : true,
14236         /**
14237          * @event beforeload
14238          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14239          * the load action will be canceled.
14240          * @param {Store} this
14241          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14242          */
14243         beforeload : true,
14244         /**
14245          * @event beforeloadadd
14246          * Fires after a new set of Records has been loaded.
14247          * @param {Store} this
14248          * @param {Roo.data.Record[]} records The Records that were loaded
14249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14250          */
14251         beforeloadadd : true,
14252         /**
14253          * @event load
14254          * Fires after a new set of Records has been loaded, before they are added to the store.
14255          * @param {Store} this
14256          * @param {Roo.data.Record[]} records The Records that were loaded
14257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14258          * @params {Object} return from reader
14259          */
14260         load : true,
14261         /**
14262          * @event loadexception
14263          * Fires if an exception occurs in the Proxy during loading.
14264          * Called with the signature of the Proxy's "loadexception" event.
14265          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14266          * 
14267          * @param {Proxy} 
14268          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14269          * @param {Object} load options 
14270          * @param {Object} jsonData from your request (normally this contains the Exception)
14271          */
14272         loadexception : true
14273     });
14274     
14275     if(this.proxy){
14276         this.proxy = Roo.factory(this.proxy, Roo.data);
14277         this.proxy.xmodule = this.xmodule || false;
14278         this.relayEvents(this.proxy,  ["loadexception"]);
14279     }
14280     this.sortToggle = {};
14281     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14282
14283     Roo.data.Store.superclass.constructor.call(this);
14284
14285     if(this.inlineData){
14286         this.loadData(this.inlineData);
14287         delete this.inlineData;
14288     }
14289 };
14290
14291 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14292      /**
14293     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14294     * without a remote query - used by combo/forms at present.
14295     */
14296     
14297     /**
14298     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14299     */
14300     /**
14301     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14302     */
14303     /**
14304     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14305     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14306     */
14307     /**
14308     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14309     * on any HTTP request
14310     */
14311     /**
14312     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14313     */
14314     /**
14315     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14316     */
14317     multiSort: false,
14318     /**
14319     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14320     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14321     */
14322     remoteSort : false,
14323
14324     /**
14325     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14326      * loaded or when a record is removed. (defaults to false).
14327     */
14328     pruneModifiedRecords : false,
14329
14330     // private
14331     lastOptions : null,
14332
14333     /**
14334      * Add Records to the Store and fires the add event.
14335      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14336      */
14337     add : function(records){
14338         records = [].concat(records);
14339         for(var i = 0, len = records.length; i < len; i++){
14340             records[i].join(this);
14341         }
14342         var index = this.data.length;
14343         this.data.addAll(records);
14344         this.fireEvent("add", this, records, index);
14345     },
14346
14347     /**
14348      * Remove a Record from the Store and fires the remove event.
14349      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14350      */
14351     remove : function(record){
14352         var index = this.data.indexOf(record);
14353         this.data.removeAt(index);
14354  
14355         if(this.pruneModifiedRecords){
14356             this.modified.remove(record);
14357         }
14358         this.fireEvent("remove", this, record, index);
14359     },
14360
14361     /**
14362      * Remove all Records from the Store and fires the clear event.
14363      */
14364     removeAll : function(){
14365         this.data.clear();
14366         if(this.pruneModifiedRecords){
14367             this.modified = [];
14368         }
14369         this.fireEvent("clear", this);
14370     },
14371
14372     /**
14373      * Inserts Records to the Store at the given index and fires the add event.
14374      * @param {Number} index The start index at which to insert the passed Records.
14375      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14376      */
14377     insert : function(index, records){
14378         records = [].concat(records);
14379         for(var i = 0, len = records.length; i < len; i++){
14380             this.data.insert(index, records[i]);
14381             records[i].join(this);
14382         }
14383         this.fireEvent("add", this, records, index);
14384     },
14385
14386     /**
14387      * Get the index within the cache of the passed Record.
14388      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14389      * @return {Number} The index of the passed Record. Returns -1 if not found.
14390      */
14391     indexOf : function(record){
14392         return this.data.indexOf(record);
14393     },
14394
14395     /**
14396      * Get the index within the cache of the Record with the passed id.
14397      * @param {String} id The id of the Record to find.
14398      * @return {Number} The index of the Record. Returns -1 if not found.
14399      */
14400     indexOfId : function(id){
14401         return this.data.indexOfKey(id);
14402     },
14403
14404     /**
14405      * Get the Record with the specified id.
14406      * @param {String} id The id of the Record to find.
14407      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14408      */
14409     getById : function(id){
14410         return this.data.key(id);
14411     },
14412
14413     /**
14414      * Get the Record at the specified index.
14415      * @param {Number} index The index of the Record to find.
14416      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14417      */
14418     getAt : function(index){
14419         return this.data.itemAt(index);
14420     },
14421
14422     /**
14423      * Returns a range of Records between specified indices.
14424      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14425      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14426      * @return {Roo.data.Record[]} An array of Records
14427      */
14428     getRange : function(start, end){
14429         return this.data.getRange(start, end);
14430     },
14431
14432     // private
14433     storeOptions : function(o){
14434         o = Roo.apply({}, o);
14435         delete o.callback;
14436         delete o.scope;
14437         this.lastOptions = o;
14438     },
14439
14440     /**
14441      * Loads the Record cache from the configured Proxy using the configured Reader.
14442      * <p>
14443      * If using remote paging, then the first load call must specify the <em>start</em>
14444      * and <em>limit</em> properties in the options.params property to establish the initial
14445      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14446      * <p>
14447      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14448      * and this call will return before the new data has been loaded. Perform any post-processing
14449      * in a callback function, or in a "load" event handler.</strong>
14450      * <p>
14451      * @param {Object} options An object containing properties which control loading options:<ul>
14452      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14453      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14454      * passed the following arguments:<ul>
14455      * <li>r : Roo.data.Record[]</li>
14456      * <li>options: Options object from the load call</li>
14457      * <li>success: Boolean success indicator</li></ul></li>
14458      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14459      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14460      * </ul>
14461      */
14462     load : function(options){
14463         options = options || {};
14464         if(this.fireEvent("beforeload", this, options) !== false){
14465             this.storeOptions(options);
14466             var p = Roo.apply(options.params || {}, this.baseParams);
14467             // if meta was not loaded from remote source.. try requesting it.
14468             if (!this.reader.metaFromRemote) {
14469                 p._requestMeta = 1;
14470             }
14471             if(this.sortInfo && this.remoteSort){
14472                 var pn = this.paramNames;
14473                 p[pn["sort"]] = this.sortInfo.field;
14474                 p[pn["dir"]] = this.sortInfo.direction;
14475             }
14476             if (this.multiSort) {
14477                 var pn = this.paramNames;
14478                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14479             }
14480             
14481             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14482         }
14483     },
14484
14485     /**
14486      * Reloads the Record cache from the configured Proxy using the configured Reader and
14487      * the options from the last load operation performed.
14488      * @param {Object} options (optional) An object containing properties which may override the options
14489      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14490      * the most recently used options are reused).
14491      */
14492     reload : function(options){
14493         this.load(Roo.applyIf(options||{}, this.lastOptions));
14494     },
14495
14496     // private
14497     // Called as a callback by the Reader during a load operation.
14498     loadRecords : function(o, options, success){
14499         if(!o || success === false){
14500             if(success !== false){
14501                 this.fireEvent("load", this, [], options, o);
14502             }
14503             if(options.callback){
14504                 options.callback.call(options.scope || this, [], options, false);
14505             }
14506             return;
14507         }
14508         // if data returned failure - throw an exception.
14509         if (o.success === false) {
14510             // show a message if no listener is registered.
14511             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14512                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14513             }
14514             // loadmask wil be hooked into this..
14515             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14516             return;
14517         }
14518         var r = o.records, t = o.totalRecords || r.length;
14519         
14520         this.fireEvent("beforeloadadd", this, r, options, o);
14521         
14522         if(!options || options.add !== true){
14523             if(this.pruneModifiedRecords){
14524                 this.modified = [];
14525             }
14526             for(var i = 0, len = r.length; i < len; i++){
14527                 r[i].join(this);
14528             }
14529             if(this.snapshot){
14530                 this.data = this.snapshot;
14531                 delete this.snapshot;
14532             }
14533             this.data.clear();
14534             this.data.addAll(r);
14535             this.totalLength = t;
14536             this.applySort();
14537             this.fireEvent("datachanged", this);
14538         }else{
14539             this.totalLength = Math.max(t, this.data.length+r.length);
14540             this.add(r);
14541         }
14542         
14543         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14544                 
14545             var e = new Roo.data.Record({});
14546
14547             e.set(this.parent.displayField, this.parent.emptyTitle);
14548             e.set(this.parent.valueField, '');
14549
14550             this.insert(0, e);
14551         }
14552             
14553         this.fireEvent("load", this, r, options, o);
14554         if(options.callback){
14555             options.callback.call(options.scope || this, r, options, true);
14556         }
14557     },
14558
14559
14560     /**
14561      * Loads data from a passed data block. A Reader which understands the format of the data
14562      * must have been configured in the constructor.
14563      * @param {Object} data The data block from which to read the Records.  The format of the data expected
14564      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14565      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14566      */
14567     loadData : function(o, append){
14568         var r = this.reader.readRecords(o);
14569         this.loadRecords(r, {add: append}, true);
14570     },
14571     
14572      /**
14573      * using 'cn' the nested child reader read the child array into it's child stores.
14574      * @param {Object} rec The record with a 'children array
14575      */
14576     loadDataFromChildren : function(rec)
14577     {
14578         this.loadData(this.reader.toLoadData(rec));
14579     },
14580     
14581
14582     /**
14583      * Gets the number of cached records.
14584      * <p>
14585      * <em>If using paging, this may not be the total size of the dataset. If the data object
14586      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14587      * the data set size</em>
14588      */
14589     getCount : function(){
14590         return this.data.length || 0;
14591     },
14592
14593     /**
14594      * Gets the total number of records in the dataset as returned by the server.
14595      * <p>
14596      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14597      * the dataset size</em>
14598      */
14599     getTotalCount : function(){
14600         return this.totalLength || 0;
14601     },
14602
14603     /**
14604      * Returns the sort state of the Store as an object with two properties:
14605      * <pre><code>
14606  field {String} The name of the field by which the Records are sorted
14607  direction {String} The sort order, "ASC" or "DESC"
14608      * </code></pre>
14609      */
14610     getSortState : function(){
14611         return this.sortInfo;
14612     },
14613
14614     // private
14615     applySort : function(){
14616         if(this.sortInfo && !this.remoteSort){
14617             var s = this.sortInfo, f = s.field;
14618             var st = this.fields.get(f).sortType;
14619             var fn = function(r1, r2){
14620                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14621                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14622             };
14623             this.data.sort(s.direction, fn);
14624             if(this.snapshot && this.snapshot != this.data){
14625                 this.snapshot.sort(s.direction, fn);
14626             }
14627         }
14628     },
14629
14630     /**
14631      * Sets the default sort column and order to be used by the next load operation.
14632      * @param {String} fieldName The name of the field to sort by.
14633      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14634      */
14635     setDefaultSort : function(field, dir){
14636         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14637     },
14638
14639     /**
14640      * Sort the Records.
14641      * If remote sorting is used, the sort is performed on the server, and the cache is
14642      * reloaded. If local sorting is used, the cache is sorted internally.
14643      * @param {String} fieldName The name of the field to sort by.
14644      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14645      */
14646     sort : function(fieldName, dir){
14647         var f = this.fields.get(fieldName);
14648         if(!dir){
14649             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14650             
14651             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14652                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14653             }else{
14654                 dir = f.sortDir;
14655             }
14656         }
14657         this.sortToggle[f.name] = dir;
14658         this.sortInfo = {field: f.name, direction: dir};
14659         if(!this.remoteSort){
14660             this.applySort();
14661             this.fireEvent("datachanged", this);
14662         }else{
14663             this.load(this.lastOptions);
14664         }
14665     },
14666
14667     /**
14668      * Calls the specified function for each of the Records in the cache.
14669      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14670      * Returning <em>false</em> aborts and exits the iteration.
14671      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14672      */
14673     each : function(fn, scope){
14674         this.data.each(fn, scope);
14675     },
14676
14677     /**
14678      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14679      * (e.g., during paging).
14680      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14681      */
14682     getModifiedRecords : function(){
14683         return this.modified;
14684     },
14685
14686     // private
14687     createFilterFn : function(property, value, anyMatch){
14688         if(!value.exec){ // not a regex
14689             value = String(value);
14690             if(value.length == 0){
14691                 return false;
14692             }
14693             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14694         }
14695         return function(r){
14696             return value.test(r.data[property]);
14697         };
14698     },
14699
14700     /**
14701      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14702      * @param {String} property A field on your records
14703      * @param {Number} start The record index to start at (defaults to 0)
14704      * @param {Number} end The last record index to include (defaults to length - 1)
14705      * @return {Number} The sum
14706      */
14707     sum : function(property, start, end){
14708         var rs = this.data.items, v = 0;
14709         start = start || 0;
14710         end = (end || end === 0) ? end : rs.length-1;
14711
14712         for(var i = start; i <= end; i++){
14713             v += (rs[i].data[property] || 0);
14714         }
14715         return v;
14716     },
14717
14718     /**
14719      * Filter the records by a specified property.
14720      * @param {String} field A field on your records
14721      * @param {String/RegExp} value Either a string that the field
14722      * should start with or a RegExp to test against the field
14723      * @param {Boolean} anyMatch True to match any part not just the beginning
14724      */
14725     filter : function(property, value, anyMatch){
14726         var fn = this.createFilterFn(property, value, anyMatch);
14727         return fn ? this.filterBy(fn) : this.clearFilter();
14728     },
14729
14730     /**
14731      * Filter by a function. The specified function will be called with each
14732      * record in this data source. If the function returns true the record is included,
14733      * otherwise it is filtered.
14734      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14735      * @param {Object} scope (optional) The scope of the function (defaults to this)
14736      */
14737     filterBy : function(fn, scope){
14738         this.snapshot = this.snapshot || this.data;
14739         this.data = this.queryBy(fn, scope||this);
14740         this.fireEvent("datachanged", this);
14741     },
14742
14743     /**
14744      * Query the records by a specified property.
14745      * @param {String} field A field on your records
14746      * @param {String/RegExp} value Either a string that the field
14747      * should start with or a RegExp to test against the field
14748      * @param {Boolean} anyMatch True to match any part not just the beginning
14749      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14750      */
14751     query : function(property, value, anyMatch){
14752         var fn = this.createFilterFn(property, value, anyMatch);
14753         return fn ? this.queryBy(fn) : this.data.clone();
14754     },
14755
14756     /**
14757      * Query by a function. The specified function will be called with each
14758      * record in this data source. If the function returns true the record is included
14759      * in the results.
14760      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14761      * @param {Object} scope (optional) The scope of the function (defaults to this)
14762       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14763      **/
14764     queryBy : function(fn, scope){
14765         var data = this.snapshot || this.data;
14766         return data.filterBy(fn, scope||this);
14767     },
14768
14769     /**
14770      * Collects unique values for a particular dataIndex from this store.
14771      * @param {String} dataIndex The property to collect
14772      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14773      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14774      * @return {Array} An array of the unique values
14775      **/
14776     collect : function(dataIndex, allowNull, bypassFilter){
14777         var d = (bypassFilter === true && this.snapshot) ?
14778                 this.snapshot.items : this.data.items;
14779         var v, sv, r = [], l = {};
14780         for(var i = 0, len = d.length; i < len; i++){
14781             v = d[i].data[dataIndex];
14782             sv = String(v);
14783             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14784                 l[sv] = true;
14785                 r[r.length] = v;
14786             }
14787         }
14788         return r;
14789     },
14790
14791     /**
14792      * Revert to a view of the Record cache with no filtering applied.
14793      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14794      */
14795     clearFilter : function(suppressEvent){
14796         if(this.snapshot && this.snapshot != this.data){
14797             this.data = this.snapshot;
14798             delete this.snapshot;
14799             if(suppressEvent !== true){
14800                 this.fireEvent("datachanged", this);
14801             }
14802         }
14803     },
14804
14805     // private
14806     afterEdit : function(record){
14807         if(this.modified.indexOf(record) == -1){
14808             this.modified.push(record);
14809         }
14810         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14811     },
14812     
14813     // private
14814     afterReject : function(record){
14815         this.modified.remove(record);
14816         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14817     },
14818
14819     // private
14820     afterCommit : function(record){
14821         this.modified.remove(record);
14822         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14823     },
14824
14825     /**
14826      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14827      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14828      */
14829     commitChanges : function(){
14830         var m = this.modified.slice(0);
14831         this.modified = [];
14832         for(var i = 0, len = m.length; i < len; i++){
14833             m[i].commit();
14834         }
14835     },
14836
14837     /**
14838      * Cancel outstanding changes on all changed records.
14839      */
14840     rejectChanges : function(){
14841         var m = this.modified.slice(0);
14842         this.modified = [];
14843         for(var i = 0, len = m.length; i < len; i++){
14844             m[i].reject();
14845         }
14846     },
14847
14848     onMetaChange : function(meta, rtype, o){
14849         this.recordType = rtype;
14850         this.fields = rtype.prototype.fields;
14851         delete this.snapshot;
14852         this.sortInfo = meta.sortInfo || this.sortInfo;
14853         this.modified = [];
14854         this.fireEvent('metachange', this, this.reader.meta);
14855     },
14856     
14857     moveIndex : function(data, type)
14858     {
14859         var index = this.indexOf(data);
14860         
14861         var newIndex = index + type;
14862         
14863         this.remove(data);
14864         
14865         this.insert(newIndex, data);
14866         
14867     }
14868 });/*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878
14879 /**
14880  * @class Roo.data.SimpleStore
14881  * @extends Roo.data.Store
14882  * Small helper class to make creating Stores from Array data easier.
14883  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14884  * @cfg {Array} fields An array of field definition objects, or field name strings.
14885  * @cfg {Object} an existing reader (eg. copied from another store)
14886  * @cfg {Array} data The multi-dimensional array of data
14887  * @constructor
14888  * @param {Object} config
14889  */
14890 Roo.data.SimpleStore = function(config)
14891 {
14892     Roo.data.SimpleStore.superclass.constructor.call(this, {
14893         isLocal : true,
14894         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14895                 id: config.id
14896             },
14897             Roo.data.Record.create(config.fields)
14898         ),
14899         proxy : new Roo.data.MemoryProxy(config.data)
14900     });
14901     this.load();
14902 };
14903 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14904  * Based on:
14905  * Ext JS Library 1.1.1
14906  * Copyright(c) 2006-2007, Ext JS, LLC.
14907  *
14908  * Originally Released Under LGPL - original licence link has changed is not relivant.
14909  *
14910  * Fork - LGPL
14911  * <script type="text/javascript">
14912  */
14913
14914 /**
14915 /**
14916  * @extends Roo.data.Store
14917  * @class Roo.data.JsonStore
14918  * Small helper class to make creating Stores for JSON data easier. <br/>
14919 <pre><code>
14920 var store = new Roo.data.JsonStore({
14921     url: 'get-images.php',
14922     root: 'images',
14923     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14924 });
14925 </code></pre>
14926  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14927  * JsonReader and HttpProxy (unless inline data is provided).</b>
14928  * @cfg {Array} fields An array of field definition objects, or field name strings.
14929  * @constructor
14930  * @param {Object} config
14931  */
14932 Roo.data.JsonStore = function(c){
14933     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14934         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14935         reader: new Roo.data.JsonReader(c, c.fields)
14936     }));
14937 };
14938 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14939  * Based on:
14940  * Ext JS Library 1.1.1
14941  * Copyright(c) 2006-2007, Ext JS, LLC.
14942  *
14943  * Originally Released Under LGPL - original licence link has changed is not relivant.
14944  *
14945  * Fork - LGPL
14946  * <script type="text/javascript">
14947  */
14948
14949  
14950 Roo.data.Field = function(config){
14951     if(typeof config == "string"){
14952         config = {name: config};
14953     }
14954     Roo.apply(this, config);
14955     
14956     if(!this.type){
14957         this.type = "auto";
14958     }
14959     
14960     var st = Roo.data.SortTypes;
14961     // named sortTypes are supported, here we look them up
14962     if(typeof this.sortType == "string"){
14963         this.sortType = st[this.sortType];
14964     }
14965     
14966     // set default sortType for strings and dates
14967     if(!this.sortType){
14968         switch(this.type){
14969             case "string":
14970                 this.sortType = st.asUCString;
14971                 break;
14972             case "date":
14973                 this.sortType = st.asDate;
14974                 break;
14975             default:
14976                 this.sortType = st.none;
14977         }
14978     }
14979
14980     // define once
14981     var stripRe = /[\$,%]/g;
14982
14983     // prebuilt conversion function for this field, instead of
14984     // switching every time we're reading a value
14985     if(!this.convert){
14986         var cv, dateFormat = this.dateFormat;
14987         switch(this.type){
14988             case "":
14989             case "auto":
14990             case undefined:
14991                 cv = function(v){ return v; };
14992                 break;
14993             case "string":
14994                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14995                 break;
14996             case "int":
14997                 cv = function(v){
14998                     return v !== undefined && v !== null && v !== '' ?
14999                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15000                     };
15001                 break;
15002             case "float":
15003                 cv = function(v){
15004                     return v !== undefined && v !== null && v !== '' ?
15005                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15006                     };
15007                 break;
15008             case "bool":
15009             case "boolean":
15010                 cv = function(v){ return v === true || v === "true" || v == 1; };
15011                 break;
15012             case "date":
15013                 cv = function(v){
15014                     if(!v){
15015                         return '';
15016                     }
15017                     if(v instanceof Date){
15018                         return v;
15019                     }
15020                     if(dateFormat){
15021                         if(dateFormat == "timestamp"){
15022                             return new Date(v*1000);
15023                         }
15024                         return Date.parseDate(v, dateFormat);
15025                     }
15026                     var parsed = Date.parse(v);
15027                     return parsed ? new Date(parsed) : null;
15028                 };
15029              break;
15030             
15031         }
15032         this.convert = cv;
15033     }
15034 };
15035
15036 Roo.data.Field.prototype = {
15037     dateFormat: null,
15038     defaultValue: "",
15039     mapping: null,
15040     sortType : null,
15041     sortDir : "ASC"
15042 };/*
15043  * Based on:
15044  * Ext JS Library 1.1.1
15045  * Copyright(c) 2006-2007, Ext JS, LLC.
15046  *
15047  * Originally Released Under LGPL - original licence link has changed is not relivant.
15048  *
15049  * Fork - LGPL
15050  * <script type="text/javascript">
15051  */
15052  
15053 // Base class for reading structured data from a data source.  This class is intended to be
15054 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15055
15056 /**
15057  * @class Roo.data.DataReader
15058  * Base class for reading structured data from a data source.  This class is intended to be
15059  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15060  */
15061
15062 Roo.data.DataReader = function(meta, recordType){
15063     
15064     this.meta = meta;
15065     
15066     this.recordType = recordType instanceof Array ? 
15067         Roo.data.Record.create(recordType) : recordType;
15068 };
15069
15070 Roo.data.DataReader.prototype = {
15071     
15072     
15073     readerType : 'Data',
15074      /**
15075      * Create an empty record
15076      * @param {Object} data (optional) - overlay some values
15077      * @return {Roo.data.Record} record created.
15078      */
15079     newRow :  function(d) {
15080         var da =  {};
15081         this.recordType.prototype.fields.each(function(c) {
15082             switch( c.type) {
15083                 case 'int' : da[c.name] = 0; break;
15084                 case 'date' : da[c.name] = new Date(); break;
15085                 case 'float' : da[c.name] = 0.0; break;
15086                 case 'boolean' : da[c.name] = false; break;
15087                 default : da[c.name] = ""; break;
15088             }
15089             
15090         });
15091         return new this.recordType(Roo.apply(da, d));
15092     }
15093     
15094     
15095 };/*
15096  * Based on:
15097  * Ext JS Library 1.1.1
15098  * Copyright(c) 2006-2007, Ext JS, LLC.
15099  *
15100  * Originally Released Under LGPL - original licence link has changed is not relivant.
15101  *
15102  * Fork - LGPL
15103  * <script type="text/javascript">
15104  */
15105
15106 /**
15107  * @class Roo.data.DataProxy
15108  * @extends Roo.data.Observable
15109  * This class is an abstract base class for implementations which provide retrieval of
15110  * unformatted data objects.<br>
15111  * <p>
15112  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15113  * (of the appropriate type which knows how to parse the data object) to provide a block of
15114  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15115  * <p>
15116  * Custom implementations must implement the load method as described in
15117  * {@link Roo.data.HttpProxy#load}.
15118  */
15119 Roo.data.DataProxy = function(){
15120     this.addEvents({
15121         /**
15122          * @event beforeload
15123          * Fires before a network request is made to retrieve a data object.
15124          * @param {Object} This DataProxy object.
15125          * @param {Object} params The params parameter to the load function.
15126          */
15127         beforeload : true,
15128         /**
15129          * @event load
15130          * Fires before the load method's callback is called.
15131          * @param {Object} This DataProxy object.
15132          * @param {Object} o The data object.
15133          * @param {Object} arg The callback argument object passed to the load function.
15134          */
15135         load : true,
15136         /**
15137          * @event loadexception
15138          * Fires if an Exception occurs during data retrieval.
15139          * @param {Object} This DataProxy object.
15140          * @param {Object} o The data object.
15141          * @param {Object} arg The callback argument object passed to the load function.
15142          * @param {Object} e The Exception.
15143          */
15144         loadexception : true
15145     });
15146     Roo.data.DataProxy.superclass.constructor.call(this);
15147 };
15148
15149 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15150
15151     /**
15152      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15153      */
15154 /*
15155  * Based on:
15156  * Ext JS Library 1.1.1
15157  * Copyright(c) 2006-2007, Ext JS, LLC.
15158  *
15159  * Originally Released Under LGPL - original licence link has changed is not relivant.
15160  *
15161  * Fork - LGPL
15162  * <script type="text/javascript">
15163  */
15164 /**
15165  * @class Roo.data.MemoryProxy
15166  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15167  * to the Reader when its load method is called.
15168  * @constructor
15169  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15170  */
15171 Roo.data.MemoryProxy = function(data){
15172     if (data.data) {
15173         data = data.data;
15174     }
15175     Roo.data.MemoryProxy.superclass.constructor.call(this);
15176     this.data = data;
15177 };
15178
15179 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15180     
15181     /**
15182      * Load data from the requested source (in this case an in-memory
15183      * data object passed to the constructor), read the data object into
15184      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15185      * process that block using the passed callback.
15186      * @param {Object} params This parameter is not used by the MemoryProxy class.
15187      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15188      * object into a block of Roo.data.Records.
15189      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15190      * The function must be passed <ul>
15191      * <li>The Record block object</li>
15192      * <li>The "arg" argument from the load function</li>
15193      * <li>A boolean success indicator</li>
15194      * </ul>
15195      * @param {Object} scope The scope in which to call the callback
15196      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15197      */
15198     load : function(params, reader, callback, scope, arg){
15199         params = params || {};
15200         var result;
15201         try {
15202             result = reader.readRecords(params.data ? params.data :this.data);
15203         }catch(e){
15204             this.fireEvent("loadexception", this, arg, null, e);
15205             callback.call(scope, null, arg, false);
15206             return;
15207         }
15208         callback.call(scope, result, arg, true);
15209     },
15210     
15211     // private
15212     update : function(params, records){
15213         
15214     }
15215 });/*
15216  * Based on:
15217  * Ext JS Library 1.1.1
15218  * Copyright(c) 2006-2007, Ext JS, LLC.
15219  *
15220  * Originally Released Under LGPL - original licence link has changed is not relivant.
15221  *
15222  * Fork - LGPL
15223  * <script type="text/javascript">
15224  */
15225 /**
15226  * @class Roo.data.HttpProxy
15227  * @extends Roo.data.DataProxy
15228  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15229  * configured to reference a certain URL.<br><br>
15230  * <p>
15231  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15232  * from which the running page was served.<br><br>
15233  * <p>
15234  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15235  * <p>
15236  * Be aware that to enable the browser to parse an XML document, the server must set
15237  * the Content-Type header in the HTTP response to "text/xml".
15238  * @constructor
15239  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15240  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15241  * will be used to make the request.
15242  */
15243 Roo.data.HttpProxy = function(conn){
15244     Roo.data.HttpProxy.superclass.constructor.call(this);
15245     // is conn a conn config or a real conn?
15246     this.conn = conn;
15247     this.useAjax = !conn || !conn.events;
15248   
15249 };
15250
15251 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15252     // thse are take from connection...
15253     
15254     /**
15255      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15256      */
15257     /**
15258      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15259      * extra parameters to each request made by this object. (defaults to undefined)
15260      */
15261     /**
15262      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15263      *  to each request made by this object. (defaults to undefined)
15264      */
15265     /**
15266      * @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)
15267      */
15268     /**
15269      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15270      */
15271      /**
15272      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15273      * @type Boolean
15274      */
15275   
15276
15277     /**
15278      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15279      * @type Boolean
15280      */
15281     /**
15282      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15283      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15284      * a finer-grained basis than the DataProxy events.
15285      */
15286     getConnection : function(){
15287         return this.useAjax ? Roo.Ajax : this.conn;
15288     },
15289
15290     /**
15291      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15292      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15293      * process that block using the passed callback.
15294      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15295      * for the request to the remote server.
15296      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15297      * object into a block of Roo.data.Records.
15298      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15299      * The function must be passed <ul>
15300      * <li>The Record block object</li>
15301      * <li>The "arg" argument from the load function</li>
15302      * <li>A boolean success indicator</li>
15303      * </ul>
15304      * @param {Object} scope The scope in which to call the callback
15305      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15306      */
15307     load : function(params, reader, callback, scope, arg){
15308         if(this.fireEvent("beforeload", this, params) !== false){
15309             var  o = {
15310                 params : params || {},
15311                 request: {
15312                     callback : callback,
15313                     scope : scope,
15314                     arg : arg
15315                 },
15316                 reader: reader,
15317                 callback : this.loadResponse,
15318                 scope: this
15319             };
15320             if(this.useAjax){
15321                 Roo.applyIf(o, this.conn);
15322                 if(this.activeRequest){
15323                     Roo.Ajax.abort(this.activeRequest);
15324                 }
15325                 this.activeRequest = Roo.Ajax.request(o);
15326             }else{
15327                 this.conn.request(o);
15328             }
15329         }else{
15330             callback.call(scope||this, null, arg, false);
15331         }
15332     },
15333
15334     // private
15335     loadResponse : function(o, success, response){
15336         delete this.activeRequest;
15337         if(!success){
15338             this.fireEvent("loadexception", this, o, response);
15339             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15340             return;
15341         }
15342         var result;
15343         try {
15344             result = o.reader.read(response);
15345         }catch(e){
15346             this.fireEvent("loadexception", this, o, response, e);
15347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15348             return;
15349         }
15350         
15351         this.fireEvent("load", this, o, o.request.arg);
15352         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15353     },
15354
15355     // private
15356     update : function(dataSet){
15357
15358     },
15359
15360     // private
15361     updateResponse : function(dataSet){
15362
15363     }
15364 });/*
15365  * Based on:
15366  * Ext JS Library 1.1.1
15367  * Copyright(c) 2006-2007, Ext JS, LLC.
15368  *
15369  * Originally Released Under LGPL - original licence link has changed is not relivant.
15370  *
15371  * Fork - LGPL
15372  * <script type="text/javascript">
15373  */
15374
15375 /**
15376  * @class Roo.data.ScriptTagProxy
15377  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15378  * other than the originating domain of the running page.<br><br>
15379  * <p>
15380  * <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
15381  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15382  * <p>
15383  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15384  * source code that is used as the source inside a &lt;script> tag.<br><br>
15385  * <p>
15386  * In order for the browser to process the returned data, the server must wrap the data object
15387  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15388  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15389  * depending on whether the callback name was passed:
15390  * <p>
15391  * <pre><code>
15392 boolean scriptTag = false;
15393 String cb = request.getParameter("callback");
15394 if (cb != null) {
15395     scriptTag = true;
15396     response.setContentType("text/javascript");
15397 } else {
15398     response.setContentType("application/x-json");
15399 }
15400 Writer out = response.getWriter();
15401 if (scriptTag) {
15402     out.write(cb + "(");
15403 }
15404 out.print(dataBlock.toJsonString());
15405 if (scriptTag) {
15406     out.write(");");
15407 }
15408 </pre></code>
15409  *
15410  * @constructor
15411  * @param {Object} config A configuration object.
15412  */
15413 Roo.data.ScriptTagProxy = function(config){
15414     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15415     Roo.apply(this, config);
15416     this.head = document.getElementsByTagName("head")[0];
15417 };
15418
15419 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15420
15421 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15422     /**
15423      * @cfg {String} url The URL from which to request the data object.
15424      */
15425     /**
15426      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15427      */
15428     timeout : 30000,
15429     /**
15430      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15431      * the server the name of the callback function set up by the load call to process the returned data object.
15432      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15433      * javascript output which calls this named function passing the data object as its only parameter.
15434      */
15435     callbackParam : "callback",
15436     /**
15437      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15438      * name to the request.
15439      */
15440     nocache : true,
15441
15442     /**
15443      * Load data from the configured URL, read the data object into
15444      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15445      * process that block using the passed callback.
15446      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15447      * for the request to the remote server.
15448      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15449      * object into a block of Roo.data.Records.
15450      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15451      * The function must be passed <ul>
15452      * <li>The Record block object</li>
15453      * <li>The "arg" argument from the load function</li>
15454      * <li>A boolean success indicator</li>
15455      * </ul>
15456      * @param {Object} scope The scope in which to call the callback
15457      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15458      */
15459     load : function(params, reader, callback, scope, arg){
15460         if(this.fireEvent("beforeload", this, params) !== false){
15461
15462             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15463
15464             var url = this.url;
15465             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15466             if(this.nocache){
15467                 url += "&_dc=" + (new Date().getTime());
15468             }
15469             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15470             var trans = {
15471                 id : transId,
15472                 cb : "stcCallback"+transId,
15473                 scriptId : "stcScript"+transId,
15474                 params : params,
15475                 arg : arg,
15476                 url : url,
15477                 callback : callback,
15478                 scope : scope,
15479                 reader : reader
15480             };
15481             var conn = this;
15482
15483             window[trans.cb] = function(o){
15484                 conn.handleResponse(o, trans);
15485             };
15486
15487             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15488
15489             if(this.autoAbort !== false){
15490                 this.abort();
15491             }
15492
15493             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15494
15495             var script = document.createElement("script");
15496             script.setAttribute("src", url);
15497             script.setAttribute("type", "text/javascript");
15498             script.setAttribute("id", trans.scriptId);
15499             this.head.appendChild(script);
15500
15501             this.trans = trans;
15502         }else{
15503             callback.call(scope||this, null, arg, false);
15504         }
15505     },
15506
15507     // private
15508     isLoading : function(){
15509         return this.trans ? true : false;
15510     },
15511
15512     /**
15513      * Abort the current server request.
15514      */
15515     abort : function(){
15516         if(this.isLoading()){
15517             this.destroyTrans(this.trans);
15518         }
15519     },
15520
15521     // private
15522     destroyTrans : function(trans, isLoaded){
15523         this.head.removeChild(document.getElementById(trans.scriptId));
15524         clearTimeout(trans.timeoutId);
15525         if(isLoaded){
15526             window[trans.cb] = undefined;
15527             try{
15528                 delete window[trans.cb];
15529             }catch(e){}
15530         }else{
15531             // if hasn't been loaded, wait for load to remove it to prevent script error
15532             window[trans.cb] = function(){
15533                 window[trans.cb] = undefined;
15534                 try{
15535                     delete window[trans.cb];
15536                 }catch(e){}
15537             };
15538         }
15539     },
15540
15541     // private
15542     handleResponse : function(o, trans){
15543         this.trans = false;
15544         this.destroyTrans(trans, true);
15545         var result;
15546         try {
15547             result = trans.reader.readRecords(o);
15548         }catch(e){
15549             this.fireEvent("loadexception", this, o, trans.arg, e);
15550             trans.callback.call(trans.scope||window, null, trans.arg, false);
15551             return;
15552         }
15553         this.fireEvent("load", this, o, trans.arg);
15554         trans.callback.call(trans.scope||window, result, trans.arg, true);
15555     },
15556
15557     // private
15558     handleFailure : function(trans){
15559         this.trans = false;
15560         this.destroyTrans(trans, false);
15561         this.fireEvent("loadexception", this, null, trans.arg);
15562         trans.callback.call(trans.scope||window, null, trans.arg, false);
15563     }
15564 });/*
15565  * Based on:
15566  * Ext JS Library 1.1.1
15567  * Copyright(c) 2006-2007, Ext JS, LLC.
15568  *
15569  * Originally Released Under LGPL - original licence link has changed is not relivant.
15570  *
15571  * Fork - LGPL
15572  * <script type="text/javascript">
15573  */
15574
15575 /**
15576  * @class Roo.data.JsonReader
15577  * @extends Roo.data.DataReader
15578  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15579  * based on mappings in a provided Roo.data.Record constructor.
15580  * 
15581  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15582  * in the reply previously. 
15583  * 
15584  * <p>
15585  * Example code:
15586  * <pre><code>
15587 var RecordDef = Roo.data.Record.create([
15588     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15589     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15590 ]);
15591 var myReader = new Roo.data.JsonReader({
15592     totalProperty: "results",    // The property which contains the total dataset size (optional)
15593     root: "rows",                // The property which contains an Array of row objects
15594     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15595 }, RecordDef);
15596 </code></pre>
15597  * <p>
15598  * This would consume a JSON file like this:
15599  * <pre><code>
15600 { 'results': 2, 'rows': [
15601     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15602     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15603 }
15604 </code></pre>
15605  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15606  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15607  * paged from the remote server.
15608  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15609  * @cfg {String} root name of the property which contains the Array of row objects.
15610  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15611  * @cfg {Array} fields Array of field definition objects
15612  * @constructor
15613  * Create a new JsonReader
15614  * @param {Object} meta Metadata configuration options
15615  * @param {Object} recordType Either an Array of field definition objects,
15616  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15617  */
15618 Roo.data.JsonReader = function(meta, recordType){
15619     
15620     meta = meta || {};
15621     // set some defaults:
15622     Roo.applyIf(meta, {
15623         totalProperty: 'total',
15624         successProperty : 'success',
15625         root : 'data',
15626         id : 'id'
15627     });
15628     
15629     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15630 };
15631 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15632     
15633     readerType : 'Json',
15634     
15635     /**
15636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15637      * Used by Store query builder to append _requestMeta to params.
15638      * 
15639      */
15640     metaFromRemote : false,
15641     /**
15642      * This method is only used by a DataProxy which has retrieved data from a remote server.
15643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15644      * @return {Object} data A data block which is used by an Roo.data.Store object as
15645      * a cache of Roo.data.Records.
15646      */
15647     read : function(response){
15648         var json = response.responseText;
15649        
15650         var o = /* eval:var:o */ eval("("+json+")");
15651         if(!o) {
15652             throw {message: "JsonReader.read: Json object not found"};
15653         }
15654         
15655         if(o.metaData){
15656             
15657             delete this.ef;
15658             this.metaFromRemote = true;
15659             this.meta = o.metaData;
15660             this.recordType = Roo.data.Record.create(o.metaData.fields);
15661             this.onMetaChange(this.meta, this.recordType, o);
15662         }
15663         return this.readRecords(o);
15664     },
15665
15666     // private function a store will implement
15667     onMetaChange : function(meta, recordType, o){
15668
15669     },
15670
15671     /**
15672          * @ignore
15673          */
15674     simpleAccess: function(obj, subsc) {
15675         return obj[subsc];
15676     },
15677
15678         /**
15679          * @ignore
15680          */
15681     getJsonAccessor: function(){
15682         var re = /[\[\.]/;
15683         return function(expr) {
15684             try {
15685                 return(re.test(expr))
15686                     ? new Function("obj", "return obj." + expr)
15687                     : function(obj){
15688                         return obj[expr];
15689                     };
15690             } catch(e){}
15691             return Roo.emptyFn;
15692         };
15693     }(),
15694
15695     /**
15696      * Create a data block containing Roo.data.Records from an XML document.
15697      * @param {Object} o An object which contains an Array of row objects in the property specified
15698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15699      * which contains the total size of the dataset.
15700      * @return {Object} data A data block which is used by an Roo.data.Store object as
15701      * a cache of Roo.data.Records.
15702      */
15703     readRecords : function(o){
15704         /**
15705          * After any data loads, the raw JSON data is available for further custom processing.
15706          * @type Object
15707          */
15708         this.o = o;
15709         var s = this.meta, Record = this.recordType,
15710             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15711
15712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15713         if (!this.ef) {
15714             if(s.totalProperty) {
15715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15716                 }
15717                 if(s.successProperty) {
15718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15719                 }
15720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15721                 if (s.id) {
15722                         var g = this.getJsonAccessor(s.id);
15723                         this.getId = function(rec) {
15724                                 var r = g(rec);  
15725                                 return (r === undefined || r === "") ? null : r;
15726                         };
15727                 } else {
15728                         this.getId = function(){return null;};
15729                 }
15730             this.ef = [];
15731             for(var jj = 0; jj < fl; jj++){
15732                 f = fi[jj];
15733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15734                 this.ef[jj] = this.getJsonAccessor(map);
15735             }
15736         }
15737
15738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15739         if(s.totalProperty){
15740             var vt = parseInt(this.getTotal(o), 10);
15741             if(!isNaN(vt)){
15742                 totalRecords = vt;
15743             }
15744         }
15745         if(s.successProperty){
15746             var vs = this.getSuccess(o);
15747             if(vs === false || vs === 'false'){
15748                 success = false;
15749             }
15750         }
15751         var records = [];
15752         for(var i = 0; i < c; i++){
15753                 var n = root[i];
15754             var values = {};
15755             var id = this.getId(n);
15756             for(var j = 0; j < fl; j++){
15757                 f = fi[j];
15758             var v = this.ef[j](n);
15759             if (!f.convert) {
15760                 Roo.log('missing convert for ' + f.name);
15761                 Roo.log(f);
15762                 continue;
15763             }
15764             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15765             }
15766             var record = new Record(values, id);
15767             record.json = n;
15768             records[i] = record;
15769         }
15770         return {
15771             raw : o,
15772             success : success,
15773             records : records,
15774             totalRecords : totalRecords
15775         };
15776     },
15777     // used when loading children.. @see loadDataFromChildren
15778     toLoadData: function(rec)
15779     {
15780         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15781         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15782         return { data : data, total : data.length };
15783         
15784     }
15785 });/*
15786  * Based on:
15787  * Ext JS Library 1.1.1
15788  * Copyright(c) 2006-2007, Ext JS, LLC.
15789  *
15790  * Originally Released Under LGPL - original licence link has changed is not relivant.
15791  *
15792  * Fork - LGPL
15793  * <script type="text/javascript">
15794  */
15795
15796 /**
15797  * @class Roo.data.ArrayReader
15798  * @extends Roo.data.DataReader
15799  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15800  * Each element of that Array represents a row of data fields. The
15801  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15802  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15803  * <p>
15804  * Example code:.
15805  * <pre><code>
15806 var RecordDef = Roo.data.Record.create([
15807     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15808     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15809 ]);
15810 var myReader = new Roo.data.ArrayReader({
15811     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15812 }, RecordDef);
15813 </code></pre>
15814  * <p>
15815  * This would consume an Array like this:
15816  * <pre><code>
15817 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15818   </code></pre>
15819  
15820  * @constructor
15821  * Create a new JsonReader
15822  * @param {Object} meta Metadata configuration options.
15823  * @param {Object|Array} recordType Either an Array of field definition objects
15824  * 
15825  * @cfg {Array} fields Array of field definition objects
15826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15827  * as specified to {@link Roo.data.Record#create},
15828  * or an {@link Roo.data.Record} object
15829  *
15830  * 
15831  * created using {@link Roo.data.Record#create}.
15832  */
15833 Roo.data.ArrayReader = function(meta, recordType)
15834 {    
15835     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15836 };
15837
15838 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15839     
15840       /**
15841      * Create a data block containing Roo.data.Records from an XML document.
15842      * @param {Object} o An Array of row objects which represents the dataset.
15843      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15844      * a cache of Roo.data.Records.
15845      */
15846     readRecords : function(o)
15847     {
15848         var sid = this.meta ? this.meta.id : null;
15849         var recordType = this.recordType, fields = recordType.prototype.fields;
15850         var records = [];
15851         var root = o;
15852         for(var i = 0; i < root.length; i++){
15853             var n = root[i];
15854             var values = {};
15855             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15856             for(var j = 0, jlen = fields.length; j < jlen; j++){
15857                 var f = fields.items[j];
15858                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15859                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15860                 v = f.convert(v);
15861                 values[f.name] = v;
15862             }
15863             var record = new recordType(values, id);
15864             record.json = n;
15865             records[records.length] = record;
15866         }
15867         return {
15868             records : records,
15869             totalRecords : records.length
15870         };
15871     },
15872     // used when loading children.. @see loadDataFromChildren
15873     toLoadData: function(rec)
15874     {
15875         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15876         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15877         
15878     }
15879     
15880     
15881 });/*
15882  * - LGPL
15883  * * 
15884  */
15885
15886 /**
15887  * @class Roo.bootstrap.ComboBox
15888  * @extends Roo.bootstrap.TriggerField
15889  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15890  * @cfg {Boolean} append (true|false) default false
15891  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15892  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15893  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15894  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15895  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15896  * @cfg {Boolean} animate default true
15897  * @cfg {Boolean} emptyResultText only for touch device
15898  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15899  * @cfg {String} emptyTitle default ''
15900  * @cfg {Number} width fixed with? experimental
15901  * @constructor
15902  * Create a new ComboBox.
15903  * @param {Object} config Configuration options
15904  */
15905 Roo.bootstrap.ComboBox = function(config){
15906     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15907     this.addEvents({
15908         /**
15909          * @event expand
15910          * Fires when the dropdown list is expanded
15911         * @param {Roo.bootstrap.ComboBox} combo This combo box
15912         */
15913         'expand' : true,
15914         /**
15915          * @event collapse
15916          * Fires when the dropdown list is collapsed
15917         * @param {Roo.bootstrap.ComboBox} combo This combo box
15918         */
15919         'collapse' : true,
15920         /**
15921          * @event beforeselect
15922          * Fires before a list item is selected. Return false to cancel the selection.
15923         * @param {Roo.bootstrap.ComboBox} combo This combo box
15924         * @param {Roo.data.Record} record The data record returned from the underlying store
15925         * @param {Number} index The index of the selected item in the dropdown list
15926         */
15927         'beforeselect' : true,
15928         /**
15929          * @event select
15930          * Fires when a list item is selected
15931         * @param {Roo.bootstrap.ComboBox} combo This combo box
15932         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15933         * @param {Number} index The index of the selected item in the dropdown list
15934         */
15935         'select' : true,
15936         /**
15937          * @event beforequery
15938          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15939          * The event object passed has these properties:
15940         * @param {Roo.bootstrap.ComboBox} combo This combo box
15941         * @param {String} query The query
15942         * @param {Boolean} forceAll true to force "all" query
15943         * @param {Boolean} cancel true to cancel the query
15944         * @param {Object} e The query event object
15945         */
15946         'beforequery': true,
15947          /**
15948          * @event add
15949          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15950         * @param {Roo.bootstrap.ComboBox} combo This combo box
15951         */
15952         'add' : true,
15953         /**
15954          * @event edit
15955          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15956         * @param {Roo.bootstrap.ComboBox} combo This combo box
15957         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15958         */
15959         'edit' : true,
15960         /**
15961          * @event remove
15962          * Fires when the remove value from the combobox array
15963         * @param {Roo.bootstrap.ComboBox} combo This combo box
15964         */
15965         'remove' : true,
15966         /**
15967          * @event afterremove
15968          * Fires when the remove value from the combobox array
15969         * @param {Roo.bootstrap.ComboBox} combo This combo box
15970         */
15971         'afterremove' : true,
15972         /**
15973          * @event specialfilter
15974          * Fires when specialfilter
15975             * @param {Roo.bootstrap.ComboBox} combo This combo box
15976             */
15977         'specialfilter' : true,
15978         /**
15979          * @event tick
15980          * Fires when tick the element
15981             * @param {Roo.bootstrap.ComboBox} combo This combo box
15982             */
15983         'tick' : true,
15984         /**
15985          * @event touchviewdisplay
15986          * Fires when touch view require special display (default is using displayField)
15987             * @param {Roo.bootstrap.ComboBox} combo This combo box
15988             * @param {Object} cfg set html .
15989             */
15990         'touchviewdisplay' : true
15991         
15992     });
15993     
15994     this.item = [];
15995     this.tickItems = [];
15996     
15997     this.selectedIndex = -1;
15998     if(this.mode == 'local'){
15999         if(config.queryDelay === undefined){
16000             this.queryDelay = 10;
16001         }
16002         if(config.minChars === undefined){
16003             this.minChars = 0;
16004         }
16005     }
16006 };
16007
16008 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16009      
16010     /**
16011      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16012      * rendering into an Roo.Editor, defaults to false)
16013      */
16014     /**
16015      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16016      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16017      */
16018     /**
16019      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16020      */
16021     /**
16022      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16023      * the dropdown list (defaults to undefined, with no header element)
16024      */
16025
16026      /**
16027      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16028      */
16029      
16030      /**
16031      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16032      */
16033     listWidth: undefined,
16034     /**
16035      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16036      * mode = 'remote' or 'text' if mode = 'local')
16037      */
16038     displayField: undefined,
16039     
16040     /**
16041      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16042      * mode = 'remote' or 'value' if mode = 'local'). 
16043      * Note: use of a valueField requires the user make a selection
16044      * in order for a value to be mapped.
16045      */
16046     valueField: undefined,
16047     /**
16048      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16049      */
16050     modalTitle : '',
16051     
16052     /**
16053      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16054      * field's data value (defaults to the underlying DOM element's name)
16055      */
16056     hiddenName: undefined,
16057     /**
16058      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16059      */
16060     listClass: '',
16061     /**
16062      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16063      */
16064     selectedClass: 'active',
16065     
16066     /**
16067      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16068      */
16069     shadow:'sides',
16070     /**
16071      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16072      * anchor positions (defaults to 'tl-bl')
16073      */
16074     listAlign: 'tl-bl?',
16075     /**
16076      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16077      */
16078     maxHeight: 300,
16079     /**
16080      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16081      * query specified by the allQuery config option (defaults to 'query')
16082      */
16083     triggerAction: 'query',
16084     /**
16085      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16086      * (defaults to 4, does not apply if editable = false)
16087      */
16088     minChars : 4,
16089     /**
16090      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16091      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16092      */
16093     typeAhead: false,
16094     /**
16095      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16096      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16097      */
16098     queryDelay: 500,
16099     /**
16100      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16101      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16102      */
16103     pageSize: 0,
16104     /**
16105      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16106      * when editable = true (defaults to false)
16107      */
16108     selectOnFocus:false,
16109     /**
16110      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16111      */
16112     queryParam: 'query',
16113     /**
16114      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16115      * when mode = 'remote' (defaults to 'Loading...')
16116      */
16117     loadingText: 'Loading...',
16118     /**
16119      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16120      */
16121     resizable: false,
16122     /**
16123      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16124      */
16125     handleHeight : 8,
16126     /**
16127      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16128      * traditional select (defaults to true)
16129      */
16130     editable: true,
16131     /**
16132      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16133      */
16134     allQuery: '',
16135     /**
16136      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16137      */
16138     mode: 'remote',
16139     /**
16140      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16141      * listWidth has a higher value)
16142      */
16143     minListWidth : 70,
16144     /**
16145      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16146      * allow the user to set arbitrary text into the field (defaults to false)
16147      */
16148     forceSelection:false,
16149     /**
16150      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16151      * if typeAhead = true (defaults to 250)
16152      */
16153     typeAheadDelay : 250,
16154     /**
16155      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16156      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16157      */
16158     valueNotFoundText : undefined,
16159     /**
16160      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16161      */
16162     blockFocus : false,
16163     
16164     /**
16165      * @cfg {Boolean} disableClear Disable showing of clear button.
16166      */
16167     disableClear : false,
16168     /**
16169      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16170      */
16171     alwaysQuery : false,
16172     
16173     /**
16174      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16175      */
16176     multiple : false,
16177     
16178     /**
16179      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16180      */
16181     invalidClass : "has-warning",
16182     
16183     /**
16184      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16185      */
16186     validClass : "has-success",
16187     
16188     /**
16189      * @cfg {Boolean} specialFilter (true|false) special filter default false
16190      */
16191     specialFilter : false,
16192     
16193     /**
16194      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16195      */
16196     mobileTouchView : true,
16197     
16198     /**
16199      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16200      */
16201     useNativeIOS : false,
16202     
16203     /**
16204      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16205      */
16206     mobile_restrict_height : false,
16207     
16208     ios_options : false,
16209     
16210     //private
16211     addicon : false,
16212     editicon: false,
16213     
16214     page: 0,
16215     hasQuery: false,
16216     append: false,
16217     loadNext: false,
16218     autoFocus : true,
16219     tickable : false,
16220     btnPosition : 'right',
16221     triggerList : true,
16222     showToggleBtn : true,
16223     animate : true,
16224     emptyResultText: 'Empty',
16225     triggerText : 'Select',
16226     emptyTitle : '',
16227     width : false,
16228     
16229     // element that contains real text value.. (when hidden is used..)
16230     
16231     getAutoCreate : function()
16232     {   
16233         var cfg = false;
16234         //render
16235         /*
16236          * Render classic select for iso
16237          */
16238         
16239         if(Roo.isIOS && this.useNativeIOS){
16240             cfg = this.getAutoCreateNativeIOS();
16241             return cfg;
16242         }
16243         
16244         /*
16245          * Touch Devices
16246          */
16247         
16248         if(Roo.isTouch && this.mobileTouchView){
16249             cfg = this.getAutoCreateTouchView();
16250             return cfg;;
16251         }
16252         
16253         /*
16254          *  Normal ComboBox
16255          */
16256         if(!this.tickable){
16257             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16258             return cfg;
16259         }
16260         
16261         /*
16262          *  ComboBox with tickable selections
16263          */
16264              
16265         var align = this.labelAlign || this.parentLabelAlign();
16266         
16267         cfg = {
16268             cls : 'form-group roo-combobox-tickable' //input-group
16269         };
16270         
16271         var btn_text_select = '';
16272         var btn_text_done = '';
16273         var btn_text_cancel = '';
16274         
16275         if (this.btn_text_show) {
16276             btn_text_select = 'Select';
16277             btn_text_done = 'Done';
16278             btn_text_cancel = 'Cancel'; 
16279         }
16280         
16281         var buttons = {
16282             tag : 'div',
16283             cls : 'tickable-buttons',
16284             cn : [
16285                 {
16286                     tag : 'button',
16287                     type : 'button',
16288                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16289                     //html : this.triggerText
16290                     html: btn_text_select
16291                 },
16292                 {
16293                     tag : 'button',
16294                     type : 'button',
16295                     name : 'ok',
16296                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16297                     //html : 'Done'
16298                     html: btn_text_done
16299                 },
16300                 {
16301                     tag : 'button',
16302                     type : 'button',
16303                     name : 'cancel',
16304                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16305                     //html : 'Cancel'
16306                     html: btn_text_cancel
16307                 }
16308             ]
16309         };
16310         
16311         if(this.editable){
16312             buttons.cn.unshift({
16313                 tag: 'input',
16314                 cls: 'roo-select2-search-field-input'
16315             });
16316         }
16317         
16318         var _this = this;
16319         
16320         Roo.each(buttons.cn, function(c){
16321             if (_this.size) {
16322                 c.cls += ' btn-' + _this.size;
16323             }
16324
16325             if (_this.disabled) {
16326                 c.disabled = true;
16327             }
16328         });
16329         
16330         var box = {
16331             tag: 'div',
16332             style : 'display: contents',
16333             cn: [
16334                 {
16335                     tag: 'input',
16336                     type : 'hidden',
16337                     cls: 'form-hidden-field'
16338                 },
16339                 {
16340                     tag: 'ul',
16341                     cls: 'roo-select2-choices',
16342                     cn:[
16343                         {
16344                             tag: 'li',
16345                             cls: 'roo-select2-search-field',
16346                             cn: [
16347                                 buttons
16348                             ]
16349                         }
16350                     ]
16351                 }
16352             ]
16353         };
16354         
16355         var combobox = {
16356             cls: 'roo-select2-container input-group roo-select2-container-multi',
16357             cn: [
16358                 
16359                 box
16360 //                {
16361 //                    tag: 'ul',
16362 //                    cls: 'typeahead typeahead-long dropdown-menu',
16363 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16364 //                }
16365             ]
16366         };
16367         
16368         if(this.hasFeedback && !this.allowBlank){
16369             
16370             var feedback = {
16371                 tag: 'span',
16372                 cls: 'glyphicon form-control-feedback'
16373             };
16374
16375             combobox.cn.push(feedback);
16376         }
16377         
16378         
16379         
16380         var indicator = {
16381             tag : 'i',
16382             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16383             tooltip : 'This field is required'
16384         };
16385         if (Roo.bootstrap.version == 4) {
16386             indicator = {
16387                 tag : 'i',
16388                 style : 'display:none'
16389             };
16390         }
16391         if (align ==='left' && this.fieldLabel.length) {
16392             
16393             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16394             
16395             cfg.cn = [
16396                 indicator,
16397                 {
16398                     tag: 'label',
16399                     'for' :  id,
16400                     cls : 'control-label col-form-label',
16401                     html : this.fieldLabel
16402
16403                 },
16404                 {
16405                     cls : "", 
16406                     cn: [
16407                         combobox
16408                     ]
16409                 }
16410
16411             ];
16412             
16413             var labelCfg = cfg.cn[1];
16414             var contentCfg = cfg.cn[2];
16415             
16416
16417             if(this.indicatorpos == 'right'){
16418                 
16419                 cfg.cn = [
16420                     {
16421                         tag: 'label',
16422                         'for' :  id,
16423                         cls : 'control-label col-form-label',
16424                         cn : [
16425                             {
16426                                 tag : 'span',
16427                                 html : this.fieldLabel
16428                             },
16429                             indicator
16430                         ]
16431                     },
16432                     {
16433                         cls : "",
16434                         cn: [
16435                             combobox
16436                         ]
16437                     }
16438
16439                 ];
16440                 
16441                 
16442                 
16443                 labelCfg = cfg.cn[0];
16444                 contentCfg = cfg.cn[1];
16445             
16446             }
16447             
16448             if(this.labelWidth > 12){
16449                 labelCfg.style = "width: " + this.labelWidth + 'px';
16450             }
16451             if(this.width * 1 > 0){
16452                 contentCfg.style = "width: " + this.width + 'px';
16453             }
16454             if(this.labelWidth < 13 && this.labelmd == 0){
16455                 this.labelmd = this.labelWidth;
16456             }
16457             
16458             if(this.labellg > 0){
16459                 labelCfg.cls += ' col-lg-' + this.labellg;
16460                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16461             }
16462             
16463             if(this.labelmd > 0){
16464                 labelCfg.cls += ' col-md-' + this.labelmd;
16465                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16466             }
16467             
16468             if(this.labelsm > 0){
16469                 labelCfg.cls += ' col-sm-' + this.labelsm;
16470                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16471             }
16472             
16473             if(this.labelxs > 0){
16474                 labelCfg.cls += ' col-xs-' + this.labelxs;
16475                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16476             }
16477                 
16478                 
16479         } else if ( this.fieldLabel.length) {
16480 //                Roo.log(" label");
16481                  cfg.cn = [
16482                    indicator,
16483                     {
16484                         tag: 'label',
16485                         //cls : 'input-group-addon',
16486                         html : this.fieldLabel
16487                     },
16488                     combobox
16489                 ];
16490                 
16491                 if(this.indicatorpos == 'right'){
16492                     cfg.cn = [
16493                         {
16494                             tag: 'label',
16495                             //cls : 'input-group-addon',
16496                             html : this.fieldLabel
16497                         },
16498                         indicator,
16499                         combobox
16500                     ];
16501                     
16502                 }
16503
16504         } else {
16505             
16506 //                Roo.log(" no label && no align");
16507                 cfg = combobox
16508                      
16509                 
16510         }
16511          
16512         var settings=this;
16513         ['xs','sm','md','lg'].map(function(size){
16514             if (settings[size]) {
16515                 cfg.cls += ' col-' + size + '-' + settings[size];
16516             }
16517         });
16518         
16519         return cfg;
16520         
16521     },
16522     
16523     _initEventsCalled : false,
16524     
16525     // private
16526     initEvents: function()
16527     {   
16528         if (this._initEventsCalled) { // as we call render... prevent looping...
16529             return;
16530         }
16531         this._initEventsCalled = true;
16532         
16533         if (!this.store) {
16534             throw "can not find store for combo";
16535         }
16536         
16537         this.indicator = this.indicatorEl();
16538         
16539         this.store = Roo.factory(this.store, Roo.data);
16540         this.store.parent = this;
16541         
16542         // if we are building from html. then this element is so complex, that we can not really
16543         // use the rendered HTML.
16544         // so we have to trash and replace the previous code.
16545         if (Roo.XComponent.build_from_html) {
16546             // remove this element....
16547             var e = this.el.dom, k=0;
16548             while (e ) { e = e.previousSibling;  ++k;}
16549
16550             this.el.remove();
16551             
16552             this.el=false;
16553             this.rendered = false;
16554             
16555             this.render(this.parent().getChildContainer(true), k);
16556         }
16557         
16558         if(Roo.isIOS && this.useNativeIOS){
16559             this.initIOSView();
16560             return;
16561         }
16562         
16563         /*
16564          * Touch Devices
16565          */
16566         
16567         if(Roo.isTouch && this.mobileTouchView){
16568             this.initTouchView();
16569             return;
16570         }
16571         
16572         if(this.tickable){
16573             this.initTickableEvents();
16574             return;
16575         }
16576         
16577         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16578         
16579         if(this.hiddenName){
16580             
16581             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16582             
16583             this.hiddenField.dom.value =
16584                 this.hiddenValue !== undefined ? this.hiddenValue :
16585                 this.value !== undefined ? this.value : '';
16586
16587             // prevent input submission
16588             this.el.dom.removeAttribute('name');
16589             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16590              
16591              
16592         }
16593         //if(Roo.isGecko){
16594         //    this.el.dom.setAttribute('autocomplete', 'off');
16595         //}
16596         
16597         var cls = 'x-combo-list';
16598         
16599         //this.list = new Roo.Layer({
16600         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16601         //});
16602         
16603         var _this = this;
16604         
16605         (function(){
16606             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16607             _this.list.setWidth(lw);
16608         }).defer(100);
16609         
16610         this.list.on('mouseover', this.onViewOver, this);
16611         this.list.on('mousemove', this.onViewMove, this);
16612         this.list.on('scroll', this.onViewScroll, this);
16613         
16614         /*
16615         this.list.swallowEvent('mousewheel');
16616         this.assetHeight = 0;
16617
16618         if(this.title){
16619             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16620             this.assetHeight += this.header.getHeight();
16621         }
16622
16623         this.innerList = this.list.createChild({cls:cls+'-inner'});
16624         this.innerList.on('mouseover', this.onViewOver, this);
16625         this.innerList.on('mousemove', this.onViewMove, this);
16626         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16627         
16628         if(this.allowBlank && !this.pageSize && !this.disableClear){
16629             this.footer = this.list.createChild({cls:cls+'-ft'});
16630             this.pageTb = new Roo.Toolbar(this.footer);
16631            
16632         }
16633         if(this.pageSize){
16634             this.footer = this.list.createChild({cls:cls+'-ft'});
16635             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16636                     {pageSize: this.pageSize});
16637             
16638         }
16639         
16640         if (this.pageTb && this.allowBlank && !this.disableClear) {
16641             var _this = this;
16642             this.pageTb.add(new Roo.Toolbar.Fill(), {
16643                 cls: 'x-btn-icon x-btn-clear',
16644                 text: '&#160;',
16645                 handler: function()
16646                 {
16647                     _this.collapse();
16648                     _this.clearValue();
16649                     _this.onSelect(false, -1);
16650                 }
16651             });
16652         }
16653         if (this.footer) {
16654             this.assetHeight += this.footer.getHeight();
16655         }
16656         */
16657             
16658         if(!this.tpl){
16659             this.tpl = Roo.bootstrap.version == 4 ?
16660                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16661                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16662         }
16663
16664         this.view = new Roo.View(this.list, this.tpl, {
16665             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16666         });
16667         //this.view.wrapEl.setDisplayed(false);
16668         this.view.on('click', this.onViewClick, this);
16669         
16670         
16671         this.store.on('beforeload', this.onBeforeLoad, this);
16672         this.store.on('load', this.onLoad, this);
16673         this.store.on('loadexception', this.onLoadException, this);
16674         /*
16675         if(this.resizable){
16676             this.resizer = new Roo.Resizable(this.list,  {
16677                pinned:true, handles:'se'
16678             });
16679             this.resizer.on('resize', function(r, w, h){
16680                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16681                 this.listWidth = w;
16682                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16683                 this.restrictHeight();
16684             }, this);
16685             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16686         }
16687         */
16688         if(!this.editable){
16689             this.editable = true;
16690             this.setEditable(false);
16691         }
16692         
16693         /*
16694         
16695         if (typeof(this.events.add.listeners) != 'undefined') {
16696             
16697             this.addicon = this.wrap.createChild(
16698                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16699        
16700             this.addicon.on('click', function(e) {
16701                 this.fireEvent('add', this);
16702             }, this);
16703         }
16704         if (typeof(this.events.edit.listeners) != 'undefined') {
16705             
16706             this.editicon = this.wrap.createChild(
16707                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16708             if (this.addicon) {
16709                 this.editicon.setStyle('margin-left', '40px');
16710             }
16711             this.editicon.on('click', function(e) {
16712                 
16713                 // we fire even  if inothing is selected..
16714                 this.fireEvent('edit', this, this.lastData );
16715                 
16716             }, this);
16717         }
16718         */
16719         
16720         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16721             "up" : function(e){
16722                 this.inKeyMode = true;
16723                 this.selectPrev();
16724             },
16725
16726             "down" : function(e){
16727                 if(!this.isExpanded()){
16728                     this.onTriggerClick();
16729                 }else{
16730                     this.inKeyMode = true;
16731                     this.selectNext();
16732                 }
16733             },
16734
16735             "enter" : function(e){
16736 //                this.onViewClick();
16737                 //return true;
16738                 this.collapse();
16739                 
16740                 if(this.fireEvent("specialkey", this, e)){
16741                     this.onViewClick(false);
16742                 }
16743                 
16744                 return true;
16745             },
16746
16747             "esc" : function(e){
16748                 this.collapse();
16749             },
16750
16751             "tab" : function(e){
16752                 this.collapse();
16753                 
16754                 if(this.fireEvent("specialkey", this, e)){
16755                     this.onViewClick(false);
16756                 }
16757                 
16758                 return true;
16759             },
16760
16761             scope : this,
16762
16763             doRelay : function(foo, bar, hname){
16764                 if(hname == 'down' || this.scope.isExpanded()){
16765                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16766                 }
16767                 return true;
16768             },
16769
16770             forceKeyDown: true
16771         });
16772         
16773         
16774         this.queryDelay = Math.max(this.queryDelay || 10,
16775                 this.mode == 'local' ? 10 : 250);
16776         
16777         
16778         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16779         
16780         if(this.typeAhead){
16781             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16782         }
16783         if(this.editable !== false){
16784             this.inputEl().on("keyup", this.onKeyUp, this);
16785         }
16786         if(this.forceSelection){
16787             this.inputEl().on('blur', this.doForce, this);
16788         }
16789         
16790         if(this.multiple){
16791             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16792             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16793         }
16794     },
16795     
16796     initTickableEvents: function()
16797     {   
16798         this.createList();
16799         
16800         if(this.hiddenName){
16801             
16802             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16803             
16804             this.hiddenField.dom.value =
16805                 this.hiddenValue !== undefined ? this.hiddenValue :
16806                 this.value !== undefined ? this.value : '';
16807
16808             // prevent input submission
16809             this.el.dom.removeAttribute('name');
16810             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16811              
16812              
16813         }
16814         
16815 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16816         
16817         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16818         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16819         if(this.triggerList){
16820             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16821         }
16822          
16823         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16824         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16825         
16826         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16827         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16828         
16829         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16830         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16831         
16832         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16833         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16834         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16835         
16836         this.okBtn.hide();
16837         this.cancelBtn.hide();
16838         
16839         var _this = this;
16840         
16841         (function(){
16842             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16843             _this.list.setWidth(lw);
16844         }).defer(100);
16845         
16846         this.list.on('mouseover', this.onViewOver, this);
16847         this.list.on('mousemove', this.onViewMove, this);
16848         
16849         this.list.on('scroll', this.onViewScroll, this);
16850         
16851         if(!this.tpl){
16852             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16853                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16854         }
16855
16856         this.view = new Roo.View(this.list, this.tpl, {
16857             singleSelect:true,
16858             tickable:true,
16859             parent:this,
16860             store: this.store,
16861             selectedClass: this.selectedClass
16862         });
16863         
16864         //this.view.wrapEl.setDisplayed(false);
16865         this.view.on('click', this.onViewClick, this);
16866         
16867         
16868         
16869         this.store.on('beforeload', this.onBeforeLoad, this);
16870         this.store.on('load', this.onLoad, this);
16871         this.store.on('loadexception', this.onLoadException, this);
16872         
16873         if(this.editable){
16874             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16875                 "up" : function(e){
16876                     this.inKeyMode = true;
16877                     this.selectPrev();
16878                 },
16879
16880                 "down" : function(e){
16881                     this.inKeyMode = true;
16882                     this.selectNext();
16883                 },
16884
16885                 "enter" : function(e){
16886                     if(this.fireEvent("specialkey", this, e)){
16887                         this.onViewClick(false);
16888                     }
16889                     
16890                     return true;
16891                 },
16892
16893                 "esc" : function(e){
16894                     this.onTickableFooterButtonClick(e, false, false);
16895                 },
16896
16897                 "tab" : function(e){
16898                     this.fireEvent("specialkey", this, e);
16899                     
16900                     this.onTickableFooterButtonClick(e, false, false);
16901                     
16902                     return true;
16903                 },
16904
16905                 scope : this,
16906
16907                 doRelay : function(e, fn, key){
16908                     if(this.scope.isExpanded()){
16909                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16910                     }
16911                     return true;
16912                 },
16913
16914                 forceKeyDown: true
16915             });
16916         }
16917         
16918         this.queryDelay = Math.max(this.queryDelay || 10,
16919                 this.mode == 'local' ? 10 : 250);
16920         
16921         
16922         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16923         
16924         if(this.typeAhead){
16925             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16926         }
16927         
16928         if(this.editable !== false){
16929             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16930         }
16931         
16932         this.indicator = this.indicatorEl();
16933         
16934         if(this.indicator){
16935             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16936             this.indicator.hide();
16937         }
16938         
16939     },
16940
16941     onDestroy : function(){
16942         if(this.view){
16943             this.view.setStore(null);
16944             this.view.el.removeAllListeners();
16945             this.view.el.remove();
16946             this.view.purgeListeners();
16947         }
16948         if(this.list){
16949             this.list.dom.innerHTML  = '';
16950         }
16951         
16952         if(this.store){
16953             this.store.un('beforeload', this.onBeforeLoad, this);
16954             this.store.un('load', this.onLoad, this);
16955             this.store.un('loadexception', this.onLoadException, this);
16956         }
16957         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16958     },
16959
16960     // private
16961     fireKey : function(e){
16962         if(e.isNavKeyPress() && !this.list.isVisible()){
16963             this.fireEvent("specialkey", this, e);
16964         }
16965     },
16966
16967     // private
16968     onResize: function(w, h)
16969     {
16970         
16971         
16972 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16973 //        
16974 //        if(typeof w != 'number'){
16975 //            // we do not handle it!?!?
16976 //            return;
16977 //        }
16978 //        var tw = this.trigger.getWidth();
16979 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16980 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16981 //        var x = w - tw;
16982 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16983 //            
16984 //        //this.trigger.setStyle('left', x+'px');
16985 //        
16986 //        if(this.list && this.listWidth === undefined){
16987 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16988 //            this.list.setWidth(lw);
16989 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16990 //        }
16991         
16992     
16993         
16994     },
16995
16996     /**
16997      * Allow or prevent the user from directly editing the field text.  If false is passed,
16998      * the user will only be able to select from the items defined in the dropdown list.  This method
16999      * is the runtime equivalent of setting the 'editable' config option at config time.
17000      * @param {Boolean} value True to allow the user to directly edit the field text
17001      */
17002     setEditable : function(value){
17003         if(value == this.editable){
17004             return;
17005         }
17006         this.editable = value;
17007         if(!value){
17008             this.inputEl().dom.setAttribute('readOnly', true);
17009             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17010             this.inputEl().addClass('x-combo-noedit');
17011         }else{
17012             this.inputEl().dom.removeAttribute('readOnly');
17013             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17014             this.inputEl().removeClass('x-combo-noedit');
17015         }
17016     },
17017
17018     // private
17019     
17020     onBeforeLoad : function(combo,opts){
17021         if(!this.hasFocus){
17022             return;
17023         }
17024          if (!opts.add) {
17025             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17026          }
17027         this.restrictHeight();
17028         this.selectedIndex = -1;
17029     },
17030
17031     // private
17032     onLoad : function(){
17033         
17034         this.hasQuery = false;
17035         
17036         if(!this.hasFocus){
17037             return;
17038         }
17039         
17040         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17041             this.loading.hide();
17042         }
17043         
17044         if(this.store.getCount() > 0){
17045             
17046             this.expand();
17047             this.restrictHeight();
17048             if(this.lastQuery == this.allQuery){
17049                 if(this.editable && !this.tickable){
17050                     this.inputEl().dom.select();
17051                 }
17052                 
17053                 if(
17054                     !this.selectByValue(this.value, true) &&
17055                     this.autoFocus && 
17056                     (
17057                         !this.store.lastOptions ||
17058                         typeof(this.store.lastOptions.add) == 'undefined' || 
17059                         this.store.lastOptions.add != true
17060                     )
17061                 ){
17062                     this.select(0, true);
17063                 }
17064             }else{
17065                 if(this.autoFocus){
17066                     this.selectNext();
17067                 }
17068                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17069                     this.taTask.delay(this.typeAheadDelay);
17070                 }
17071             }
17072         }else{
17073             this.onEmptyResults();
17074         }
17075         
17076         //this.el.focus();
17077     },
17078     // private
17079     onLoadException : function()
17080     {
17081         this.hasQuery = false;
17082         
17083         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17084             this.loading.hide();
17085         }
17086         
17087         if(this.tickable && this.editable){
17088             return;
17089         }
17090         
17091         this.collapse();
17092         // only causes errors at present
17093         //Roo.log(this.store.reader.jsonData);
17094         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17095             // fixme
17096             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17097         //}
17098         
17099         
17100     },
17101     // private
17102     onTypeAhead : function(){
17103         if(this.store.getCount() > 0){
17104             var r = this.store.getAt(0);
17105             var newValue = r.data[this.displayField];
17106             var len = newValue.length;
17107             var selStart = this.getRawValue().length;
17108             
17109             if(selStart != len){
17110                 this.setRawValue(newValue);
17111                 this.selectText(selStart, newValue.length);
17112             }
17113         }
17114     },
17115
17116     // private
17117     onSelect : function(record, index){
17118         
17119         if(this.fireEvent('beforeselect', this, record, index) !== false){
17120         
17121             this.setFromData(index > -1 ? record.data : false);
17122             
17123             this.collapse();
17124             this.fireEvent('select', this, record, index);
17125         }
17126     },
17127
17128     /**
17129      * Returns the currently selected field value or empty string if no value is set.
17130      * @return {String} value The selected value
17131      */
17132     getValue : function()
17133     {
17134         if(Roo.isIOS && this.useNativeIOS){
17135             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17136         }
17137         
17138         if(this.multiple){
17139             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17140         }
17141         
17142         if(this.valueField){
17143             return typeof this.value != 'undefined' ? this.value : '';
17144         }else{
17145             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17146         }
17147     },
17148     
17149     getRawValue : function()
17150     {
17151         if(Roo.isIOS && this.useNativeIOS){
17152             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17153         }
17154         
17155         var v = this.inputEl().getValue();
17156         
17157         return v;
17158     },
17159
17160     /**
17161      * Clears any text/value currently set in the field
17162      */
17163     clearValue : function(){
17164         
17165         if(this.hiddenField){
17166             this.hiddenField.dom.value = '';
17167         }
17168         this.value = '';
17169         this.setRawValue('');
17170         this.lastSelectionText = '';
17171         this.lastData = false;
17172         
17173         var close = this.closeTriggerEl();
17174         
17175         if(close){
17176             close.hide();
17177         }
17178         
17179         this.validate();
17180         
17181     },
17182
17183     /**
17184      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17185      * will be displayed in the field.  If the value does not match the data value of an existing item,
17186      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17187      * Otherwise the field will be blank (although the value will still be set).
17188      * @param {String} value The value to match
17189      */
17190     setValue : function(v)
17191     {
17192         if(Roo.isIOS && this.useNativeIOS){
17193             this.setIOSValue(v);
17194             return;
17195         }
17196         
17197         if(this.multiple){
17198             this.syncValue();
17199             return;
17200         }
17201         
17202         var text = v;
17203         if(this.valueField){
17204             var r = this.findRecord(this.valueField, v);
17205             if(r){
17206                 text = r.data[this.displayField];
17207             }else if(this.valueNotFoundText !== undefined){
17208                 text = this.valueNotFoundText;
17209             }
17210         }
17211         this.lastSelectionText = text;
17212         if(this.hiddenField){
17213             this.hiddenField.dom.value = v;
17214         }
17215         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17216         this.value = v;
17217         
17218         var close = this.closeTriggerEl();
17219         
17220         if(close){
17221             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17222         }
17223         
17224         this.validate();
17225     },
17226     /**
17227      * @property {Object} the last set data for the element
17228      */
17229     
17230     lastData : false,
17231     /**
17232      * Sets the value of the field based on a object which is related to the record format for the store.
17233      * @param {Object} value the value to set as. or false on reset?
17234      */
17235     setFromData : function(o){
17236         
17237         if(this.multiple){
17238             this.addItem(o);
17239             return;
17240         }
17241             
17242         var dv = ''; // display value
17243         var vv = ''; // value value..
17244         this.lastData = o;
17245         if (this.displayField) {
17246             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17247         } else {
17248             // this is an error condition!!!
17249             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17250         }
17251         
17252         if(this.valueField){
17253             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17254         }
17255         
17256         var close = this.closeTriggerEl();
17257         
17258         if(close){
17259             if(dv.length || vv * 1 > 0){
17260                 close.show() ;
17261                 this.blockFocus=true;
17262             } else {
17263                 close.hide();
17264             }             
17265         }
17266         
17267         if(this.hiddenField){
17268             this.hiddenField.dom.value = vv;
17269             
17270             this.lastSelectionText = dv;
17271             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17272             this.value = vv;
17273             return;
17274         }
17275         // no hidden field.. - we store the value in 'value', but still display
17276         // display field!!!!
17277         this.lastSelectionText = dv;
17278         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17279         this.value = vv;
17280         
17281         
17282         
17283     },
17284     // private
17285     reset : function(){
17286         // overridden so that last data is reset..
17287         
17288         if(this.multiple){
17289             this.clearItem();
17290             return;
17291         }
17292         
17293         this.setValue(this.originalValue);
17294         //this.clearInvalid();
17295         this.lastData = false;
17296         if (this.view) {
17297             this.view.clearSelections();
17298         }
17299         
17300         this.validate();
17301     },
17302     // private
17303     findRecord : function(prop, value){
17304         var record;
17305         if(this.store.getCount() > 0){
17306             this.store.each(function(r){
17307                 if(r.data[prop] == value){
17308                     record = r;
17309                     return false;
17310                 }
17311                 return true;
17312             });
17313         }
17314         return record;
17315     },
17316     
17317     getName: function()
17318     {
17319         // returns hidden if it's set..
17320         if (!this.rendered) {return ''};
17321         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17322         
17323     },
17324     // private
17325     onViewMove : function(e, t){
17326         this.inKeyMode = false;
17327     },
17328
17329     // private
17330     onViewOver : function(e, t){
17331         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17332             return;
17333         }
17334         var item = this.view.findItemFromChild(t);
17335         
17336         if(item){
17337             var index = this.view.indexOf(item);
17338             this.select(index, false);
17339         }
17340     },
17341
17342     // private
17343     onViewClick : function(view, doFocus, el, e)
17344     {
17345         var index = this.view.getSelectedIndexes()[0];
17346         
17347         var r = this.store.getAt(index);
17348         
17349         if(this.tickable){
17350             
17351             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17352                 return;
17353             }
17354             
17355             var rm = false;
17356             var _this = this;
17357             
17358             Roo.each(this.tickItems, function(v,k){
17359                 
17360                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17361                     Roo.log(v);
17362                     _this.tickItems.splice(k, 1);
17363                     
17364                     if(typeof(e) == 'undefined' && view == false){
17365                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17366                     }
17367                     
17368                     rm = true;
17369                     return;
17370                 }
17371             });
17372             
17373             if(rm){
17374                 return;
17375             }
17376             
17377             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17378                 this.tickItems.push(r.data);
17379             }
17380             
17381             if(typeof(e) == 'undefined' && view == false){
17382                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17383             }
17384                     
17385             return;
17386         }
17387         
17388         if(r){
17389             this.onSelect(r, index);
17390         }
17391         if(doFocus !== false && !this.blockFocus){
17392             this.inputEl().focus();
17393         }
17394     },
17395
17396     // private
17397     restrictHeight : function(){
17398         //this.innerList.dom.style.height = '';
17399         //var inner = this.innerList.dom;
17400         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17401         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17402         //this.list.beginUpdate();
17403         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17404         this.list.alignTo(this.inputEl(), this.listAlign);
17405         this.list.alignTo(this.inputEl(), this.listAlign);
17406         //this.list.endUpdate();
17407     },
17408
17409     // private
17410     onEmptyResults : function(){
17411         
17412         if(this.tickable && this.editable){
17413             this.hasFocus = false;
17414             this.restrictHeight();
17415             return;
17416         }
17417         
17418         this.collapse();
17419     },
17420
17421     /**
17422      * Returns true if the dropdown list is expanded, else false.
17423      */
17424     isExpanded : function(){
17425         return this.list.isVisible();
17426     },
17427
17428     /**
17429      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17430      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17431      * @param {String} value The data value of the item to select
17432      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17433      * selected item if it is not currently in view (defaults to true)
17434      * @return {Boolean} True if the value matched an item in the list, else false
17435      */
17436     selectByValue : function(v, scrollIntoView){
17437         if(v !== undefined && v !== null){
17438             var r = this.findRecord(this.valueField || this.displayField, v);
17439             if(r){
17440                 this.select(this.store.indexOf(r), scrollIntoView);
17441                 return true;
17442             }
17443         }
17444         return false;
17445     },
17446
17447     /**
17448      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17449      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17450      * @param {Number} index The zero-based index of the list item to select
17451      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17452      * selected item if it is not currently in view (defaults to true)
17453      */
17454     select : function(index, scrollIntoView){
17455         this.selectedIndex = index;
17456         this.view.select(index);
17457         if(scrollIntoView !== false){
17458             var el = this.view.getNode(index);
17459             /*
17460              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17461              */
17462             if(el){
17463                 this.list.scrollChildIntoView(el, false);
17464             }
17465         }
17466     },
17467
17468     // private
17469     selectNext : function(){
17470         var ct = this.store.getCount();
17471         if(ct > 0){
17472             if(this.selectedIndex == -1){
17473                 this.select(0);
17474             }else if(this.selectedIndex < ct-1){
17475                 this.select(this.selectedIndex+1);
17476             }
17477         }
17478     },
17479
17480     // private
17481     selectPrev : function(){
17482         var ct = this.store.getCount();
17483         if(ct > 0){
17484             if(this.selectedIndex == -1){
17485                 this.select(0);
17486             }else if(this.selectedIndex != 0){
17487                 this.select(this.selectedIndex-1);
17488             }
17489         }
17490     },
17491
17492     // private
17493     onKeyUp : function(e){
17494         if(this.editable !== false && !e.isSpecialKey()){
17495             this.lastKey = e.getKey();
17496             this.dqTask.delay(this.queryDelay);
17497         }
17498     },
17499
17500     // private
17501     validateBlur : function(){
17502         return !this.list || !this.list.isVisible();   
17503     },
17504
17505     // private
17506     initQuery : function(){
17507         
17508         var v = this.getRawValue();
17509         
17510         if(this.tickable && this.editable){
17511             v = this.tickableInputEl().getValue();
17512         }
17513         
17514         this.doQuery(v);
17515     },
17516
17517     // private
17518     doForce : function(){
17519         if(this.inputEl().dom.value.length > 0){
17520             this.inputEl().dom.value =
17521                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17522              
17523         }
17524     },
17525
17526     /**
17527      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17528      * query allowing the query action to be canceled if needed.
17529      * @param {String} query The SQL query to execute
17530      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17531      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17532      * saved in the current store (defaults to false)
17533      */
17534     doQuery : function(q, forceAll){
17535         
17536         if(q === undefined || q === null){
17537             q = '';
17538         }
17539         var qe = {
17540             query: q,
17541             forceAll: forceAll,
17542             combo: this,
17543             cancel:false
17544         };
17545         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17546             return false;
17547         }
17548         q = qe.query;
17549         
17550         forceAll = qe.forceAll;
17551         if(forceAll === true || (q.length >= this.minChars)){
17552             
17553             this.hasQuery = true;
17554             
17555             if(this.lastQuery != q || this.alwaysQuery){
17556                 this.lastQuery = q;
17557                 if(this.mode == 'local'){
17558                     this.selectedIndex = -1;
17559                     if(forceAll){
17560                         this.store.clearFilter();
17561                     }else{
17562                         
17563                         if(this.specialFilter){
17564                             this.fireEvent('specialfilter', this);
17565                             this.onLoad();
17566                             return;
17567                         }
17568                         
17569                         this.store.filter(this.displayField, q);
17570                     }
17571                     
17572                     this.store.fireEvent("datachanged", this.store);
17573                     
17574                     this.onLoad();
17575                     
17576                     
17577                 }else{
17578                     
17579                     this.store.baseParams[this.queryParam] = q;
17580                     
17581                     var options = {params : this.getParams(q)};
17582                     
17583                     if(this.loadNext){
17584                         options.add = true;
17585                         options.params.start = this.page * this.pageSize;
17586                     }
17587                     
17588                     this.store.load(options);
17589                     
17590                     /*
17591                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17592                      *  we should expand the list on onLoad
17593                      *  so command out it
17594                      */
17595 //                    this.expand();
17596                 }
17597             }else{
17598                 this.selectedIndex = -1;
17599                 this.onLoad();   
17600             }
17601         }
17602         
17603         this.loadNext = false;
17604     },
17605     
17606     // private
17607     getParams : function(q){
17608         var p = {};
17609         //p[this.queryParam] = q;
17610         
17611         if(this.pageSize){
17612             p.start = 0;
17613             p.limit = this.pageSize;
17614         }
17615         return p;
17616     },
17617
17618     /**
17619      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17620      */
17621     collapse : function(){
17622         if(!this.isExpanded()){
17623             return;
17624         }
17625         
17626         this.list.hide();
17627         
17628         this.hasFocus = false;
17629         
17630         if(this.tickable){
17631             this.okBtn.hide();
17632             this.cancelBtn.hide();
17633             this.trigger.show();
17634             
17635             if(this.editable){
17636                 this.tickableInputEl().dom.value = '';
17637                 this.tickableInputEl().blur();
17638             }
17639             
17640         }
17641         
17642         Roo.get(document).un('mousedown', this.collapseIf, this);
17643         Roo.get(document).un('mousewheel', this.collapseIf, this);
17644         if (!this.editable) {
17645             Roo.get(document).un('keydown', this.listKeyPress, this);
17646         }
17647         this.fireEvent('collapse', this);
17648         
17649         this.validate();
17650     },
17651
17652     // private
17653     collapseIf : function(e){
17654         var in_combo  = e.within(this.el);
17655         var in_list =  e.within(this.list);
17656         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17657         
17658         if (in_combo || in_list || is_list) {
17659             //e.stopPropagation();
17660             return;
17661         }
17662         
17663         if(this.tickable){
17664             this.onTickableFooterButtonClick(e, false, false);
17665         }
17666
17667         this.collapse();
17668         
17669     },
17670
17671     /**
17672      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17673      */
17674     expand : function(){
17675        
17676         if(this.isExpanded() || !this.hasFocus){
17677             return;
17678         }
17679         
17680         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17681         this.list.setWidth(lw);
17682         
17683         Roo.log('expand');
17684         
17685         this.list.show();
17686         
17687         this.restrictHeight();
17688         
17689         if(this.tickable){
17690             
17691             this.tickItems = Roo.apply([], this.item);
17692             
17693             this.okBtn.show();
17694             this.cancelBtn.show();
17695             this.trigger.hide();
17696             
17697             if(this.editable){
17698                 this.tickableInputEl().focus();
17699             }
17700             
17701         }
17702         
17703         Roo.get(document).on('mousedown', this.collapseIf, this);
17704         Roo.get(document).on('mousewheel', this.collapseIf, this);
17705         if (!this.editable) {
17706             Roo.get(document).on('keydown', this.listKeyPress, this);
17707         }
17708         
17709         this.fireEvent('expand', this);
17710     },
17711
17712     // private
17713     // Implements the default empty TriggerField.onTriggerClick function
17714     onTriggerClick : function(e)
17715     {
17716         Roo.log('trigger click');
17717         
17718         if(this.disabled || !this.triggerList){
17719             return;
17720         }
17721         
17722         this.page = 0;
17723         this.loadNext = false;
17724         
17725         if(this.isExpanded()){
17726             this.collapse();
17727             if (!this.blockFocus) {
17728                 this.inputEl().focus();
17729             }
17730             
17731         }else {
17732             this.hasFocus = true;
17733             if(this.triggerAction == 'all') {
17734                 this.doQuery(this.allQuery, true);
17735             } else {
17736                 this.doQuery(this.getRawValue());
17737             }
17738             if (!this.blockFocus) {
17739                 this.inputEl().focus();
17740             }
17741         }
17742     },
17743     
17744     onTickableTriggerClick : function(e)
17745     {
17746         if(this.disabled){
17747             return;
17748         }
17749         
17750         this.page = 0;
17751         this.loadNext = false;
17752         this.hasFocus = true;
17753         
17754         if(this.triggerAction == 'all') {
17755             this.doQuery(this.allQuery, true);
17756         } else {
17757             this.doQuery(this.getRawValue());
17758         }
17759     },
17760     
17761     onSearchFieldClick : function(e)
17762     {
17763         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17764             this.onTickableFooterButtonClick(e, false, false);
17765             return;
17766         }
17767         
17768         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17769             return;
17770         }
17771         
17772         this.page = 0;
17773         this.loadNext = false;
17774         this.hasFocus = true;
17775         
17776         if(this.triggerAction == 'all') {
17777             this.doQuery(this.allQuery, true);
17778         } else {
17779             this.doQuery(this.getRawValue());
17780         }
17781     },
17782     
17783     listKeyPress : function(e)
17784     {
17785         //Roo.log('listkeypress');
17786         // scroll to first matching element based on key pres..
17787         if (e.isSpecialKey()) {
17788             return false;
17789         }
17790         var k = String.fromCharCode(e.getKey()).toUpperCase();
17791         //Roo.log(k);
17792         var match  = false;
17793         var csel = this.view.getSelectedNodes();
17794         var cselitem = false;
17795         if (csel.length) {
17796             var ix = this.view.indexOf(csel[0]);
17797             cselitem  = this.store.getAt(ix);
17798             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17799                 cselitem = false;
17800             }
17801             
17802         }
17803         
17804         this.store.each(function(v) { 
17805             if (cselitem) {
17806                 // start at existing selection.
17807                 if (cselitem.id == v.id) {
17808                     cselitem = false;
17809                 }
17810                 return true;
17811             }
17812                 
17813             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17814                 match = this.store.indexOf(v);
17815                 return false;
17816             }
17817             return true;
17818         }, this);
17819         
17820         if (match === false) {
17821             return true; // no more action?
17822         }
17823         // scroll to?
17824         this.view.select(match);
17825         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17826         sn.scrollIntoView(sn.dom.parentNode, false);
17827     },
17828     
17829     onViewScroll : function(e, t){
17830         
17831         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){
17832             return;
17833         }
17834         
17835         this.hasQuery = true;
17836         
17837         this.loading = this.list.select('.loading', true).first();
17838         
17839         if(this.loading === null){
17840             this.list.createChild({
17841                 tag: 'div',
17842                 cls: 'loading roo-select2-more-results roo-select2-active',
17843                 html: 'Loading more results...'
17844             });
17845             
17846             this.loading = this.list.select('.loading', true).first();
17847             
17848             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17849             
17850             this.loading.hide();
17851         }
17852         
17853         this.loading.show();
17854         
17855         var _combo = this;
17856         
17857         this.page++;
17858         this.loadNext = true;
17859         
17860         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17861         
17862         return;
17863     },
17864     
17865     addItem : function(o)
17866     {   
17867         var dv = ''; // display value
17868         
17869         if (this.displayField) {
17870             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17871         } else {
17872             // this is an error condition!!!
17873             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17874         }
17875         
17876         if(!dv.length){
17877             return;
17878         }
17879         
17880         var choice = this.choices.createChild({
17881             tag: 'li',
17882             cls: 'roo-select2-search-choice',
17883             cn: [
17884                 {
17885                     tag: 'div',
17886                     html: dv
17887                 },
17888                 {
17889                     tag: 'a',
17890                     href: '#',
17891                     cls: 'roo-select2-search-choice-close fa fa-times',
17892                     tabindex: '-1'
17893                 }
17894             ]
17895             
17896         }, this.searchField);
17897         
17898         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17899         
17900         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17901         
17902         this.item.push(o);
17903         
17904         this.lastData = o;
17905         
17906         this.syncValue();
17907         
17908         this.inputEl().dom.value = '';
17909         
17910         this.validate();
17911     },
17912     
17913     onRemoveItem : function(e, _self, o)
17914     {
17915         e.preventDefault();
17916         
17917         this.lastItem = Roo.apply([], this.item);
17918         
17919         var index = this.item.indexOf(o.data) * 1;
17920         
17921         if( index < 0){
17922             Roo.log('not this item?!');
17923             return;
17924         }
17925         
17926         this.item.splice(index, 1);
17927         o.item.remove();
17928         
17929         this.syncValue();
17930         
17931         this.fireEvent('remove', this, e);
17932         
17933         this.validate();
17934         
17935     },
17936     
17937     syncValue : function()
17938     {
17939         if(!this.item.length){
17940             this.clearValue();
17941             return;
17942         }
17943             
17944         var value = [];
17945         var _this = this;
17946         Roo.each(this.item, function(i){
17947             if(_this.valueField){
17948                 value.push(i[_this.valueField]);
17949                 return;
17950             }
17951
17952             value.push(i);
17953         });
17954
17955         this.value = value.join(',');
17956
17957         if(this.hiddenField){
17958             this.hiddenField.dom.value = this.value;
17959         }
17960         
17961         this.store.fireEvent("datachanged", this.store);
17962         
17963         this.validate();
17964     },
17965     
17966     clearItem : function()
17967     {
17968         if(!this.multiple){
17969             return;
17970         }
17971         
17972         this.item = [];
17973         
17974         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17975            c.remove();
17976         });
17977         
17978         this.syncValue();
17979         
17980         this.validate();
17981         
17982         if(this.tickable && !Roo.isTouch){
17983             this.view.refresh();
17984         }
17985     },
17986     
17987     inputEl: function ()
17988     {
17989         if(Roo.isIOS && this.useNativeIOS){
17990             return this.el.select('select.roo-ios-select', true).first();
17991         }
17992         
17993         if(Roo.isTouch && this.mobileTouchView){
17994             return this.el.select('input.form-control',true).first();
17995         }
17996         
17997         if(this.tickable){
17998             return this.searchField;
17999         }
18000         
18001         return this.el.select('input.form-control',true).first();
18002     },
18003     
18004     onTickableFooterButtonClick : function(e, btn, el)
18005     {
18006         e.preventDefault();
18007         
18008         this.lastItem = Roo.apply([], this.item);
18009         
18010         if(btn && btn.name == 'cancel'){
18011             this.tickItems = Roo.apply([], this.item);
18012             this.collapse();
18013             return;
18014         }
18015         
18016         this.clearItem();
18017         
18018         var _this = this;
18019         
18020         Roo.each(this.tickItems, function(o){
18021             _this.addItem(o);
18022         });
18023         
18024         this.collapse();
18025         
18026     },
18027     
18028     validate : function()
18029     {
18030         if(this.getVisibilityEl().hasClass('hidden')){
18031             return true;
18032         }
18033         
18034         var v = this.getRawValue();
18035         
18036         if(this.multiple){
18037             v = this.getValue();
18038         }
18039         
18040         if(this.disabled || this.allowBlank || v.length){
18041             this.markValid();
18042             return true;
18043         }
18044         
18045         this.markInvalid();
18046         return false;
18047     },
18048     
18049     tickableInputEl : function()
18050     {
18051         if(!this.tickable || !this.editable){
18052             return this.inputEl();
18053         }
18054         
18055         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18056     },
18057     
18058     
18059     getAutoCreateTouchView : function()
18060     {
18061         var id = Roo.id();
18062         
18063         var cfg = {
18064             cls: 'form-group' //input-group
18065         };
18066         
18067         var input =  {
18068             tag: 'input',
18069             id : id,
18070             type : this.inputType,
18071             cls : 'form-control x-combo-noedit',
18072             autocomplete: 'new-password',
18073             placeholder : this.placeholder || '',
18074             readonly : true
18075         };
18076         
18077         if (this.name) {
18078             input.name = this.name;
18079         }
18080         
18081         if (this.size) {
18082             input.cls += ' input-' + this.size;
18083         }
18084         
18085         if (this.disabled) {
18086             input.disabled = true;
18087         }
18088         
18089         var inputblock = {
18090             cls : 'roo-combobox-wrap',
18091             cn : [
18092                 input
18093             ]
18094         };
18095         
18096         if(this.before){
18097             inputblock.cls += ' input-group';
18098             
18099             inputblock.cn.unshift({
18100                 tag :'span',
18101                 cls : 'input-group-addon input-group-prepend input-group-text',
18102                 html : this.before
18103             });
18104         }
18105         
18106         if(this.removable && !this.multiple){
18107             inputblock.cls += ' roo-removable';
18108             
18109             inputblock.cn.push({
18110                 tag: 'button',
18111                 html : 'x',
18112                 cls : 'roo-combo-removable-btn close'
18113             });
18114         }
18115
18116         if(this.hasFeedback && !this.allowBlank){
18117             
18118             inputblock.cls += ' has-feedback';
18119             
18120             inputblock.cn.push({
18121                 tag: 'span',
18122                 cls: 'glyphicon form-control-feedback'
18123             });
18124             
18125         }
18126         
18127         if (this.after) {
18128             
18129             inputblock.cls += (this.before) ? '' : ' input-group';
18130             
18131             inputblock.cn.push({
18132                 tag :'span',
18133                 cls : 'input-group-addon input-group-append input-group-text',
18134                 html : this.after
18135             });
18136         }
18137
18138         
18139         var ibwrap = inputblock;
18140         
18141         if(this.multiple){
18142             ibwrap = {
18143                 tag: 'ul',
18144                 cls: 'roo-select2-choices',
18145                 cn:[
18146                     {
18147                         tag: 'li',
18148                         cls: 'roo-select2-search-field',
18149                         cn: [
18150
18151                             inputblock
18152                         ]
18153                     }
18154                 ]
18155             };
18156         
18157             
18158         }
18159         
18160         var combobox = {
18161             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18162             cn: [
18163                 {
18164                     tag: 'input',
18165                     type : 'hidden',
18166                     cls: 'form-hidden-field'
18167                 },
18168                 ibwrap
18169             ]
18170         };
18171         
18172         if(!this.multiple && this.showToggleBtn){
18173             
18174             var caret = {
18175                 cls: 'caret'
18176             };
18177             
18178             if (this.caret != false) {
18179                 caret = {
18180                      tag: 'i',
18181                      cls: 'fa fa-' + this.caret
18182                 };
18183                 
18184             }
18185             
18186             combobox.cn.push({
18187                 tag :'span',
18188                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18189                 cn : [
18190                     Roo.bootstrap.version == 3 ? caret : '',
18191                     {
18192                         tag: 'span',
18193                         cls: 'combobox-clear',
18194                         cn  : [
18195                             {
18196                                 tag : 'i',
18197                                 cls: 'icon-remove'
18198                             }
18199                         ]
18200                     }
18201                 ]
18202
18203             })
18204         }
18205         
18206         if(this.multiple){
18207             combobox.cls += ' roo-select2-container-multi';
18208         }
18209         
18210         var required =  this.allowBlank ?  {
18211                     tag : 'i',
18212                     style: 'display: none'
18213                 } : {
18214                    tag : 'i',
18215                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18216                    tooltip : 'This field is required'
18217                 };
18218         
18219         var align = this.labelAlign || this.parentLabelAlign();
18220         
18221         if (align ==='left' && this.fieldLabel.length) {
18222
18223             cfg.cn = [
18224                 required,
18225                 {
18226                     tag: 'label',
18227                     cls : 'control-label col-form-label',
18228                     html : this.fieldLabel
18229
18230                 },
18231                 {
18232                     cls : 'roo-combobox-wrap ', 
18233                     cn: [
18234                         combobox
18235                     ]
18236                 }
18237             ];
18238             
18239             var labelCfg = cfg.cn[1];
18240             var contentCfg = cfg.cn[2];
18241             
18242
18243             if(this.indicatorpos == 'right'){
18244                 cfg.cn = [
18245                     {
18246                         tag: 'label',
18247                         'for' :  id,
18248                         cls : 'control-label col-form-label',
18249                         cn : [
18250                             {
18251                                 tag : 'span',
18252                                 html : this.fieldLabel
18253                             },
18254                             required
18255                         ]
18256                     },
18257                     {
18258                         cls : "roo-combobox-wrap ",
18259                         cn: [
18260                             combobox
18261                         ]
18262                     }
18263
18264                 ];
18265                 
18266                 labelCfg = cfg.cn[0];
18267                 contentCfg = cfg.cn[1];
18268             }
18269             
18270            
18271             
18272             if(this.labelWidth > 12){
18273                 labelCfg.style = "width: " + this.labelWidth + 'px';
18274             }
18275            
18276             if(this.labelWidth < 13 && this.labelmd == 0){
18277                 this.labelmd = this.labelWidth;
18278             }
18279             
18280             if(this.labellg > 0){
18281                 labelCfg.cls += ' col-lg-' + this.labellg;
18282                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18283             }
18284             
18285             if(this.labelmd > 0){
18286                 labelCfg.cls += ' col-md-' + this.labelmd;
18287                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18288             }
18289             
18290             if(this.labelsm > 0){
18291                 labelCfg.cls += ' col-sm-' + this.labelsm;
18292                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18293             }
18294             
18295             if(this.labelxs > 0){
18296                 labelCfg.cls += ' col-xs-' + this.labelxs;
18297                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18298             }
18299                 
18300                 
18301         } else if ( this.fieldLabel.length) {
18302             cfg.cn = [
18303                required,
18304                 {
18305                     tag: 'label',
18306                     cls : 'control-label',
18307                     html : this.fieldLabel
18308
18309                 },
18310                 {
18311                     cls : '', 
18312                     cn: [
18313                         combobox
18314                     ]
18315                 }
18316             ];
18317             
18318             if(this.indicatorpos == 'right'){
18319                 cfg.cn = [
18320                     {
18321                         tag: 'label',
18322                         cls : 'control-label',
18323                         html : this.fieldLabel,
18324                         cn : [
18325                             required
18326                         ]
18327                     },
18328                     {
18329                         cls : '', 
18330                         cn: [
18331                             combobox
18332                         ]
18333                     }
18334                 ];
18335             }
18336         } else {
18337             cfg.cn = combobox;    
18338         }
18339         
18340         
18341         var settings = this;
18342         
18343         ['xs','sm','md','lg'].map(function(size){
18344             if (settings[size]) {
18345                 cfg.cls += ' col-' + size + '-' + settings[size];
18346             }
18347         });
18348         
18349         return cfg;
18350     },
18351     
18352     initTouchView : function()
18353     {
18354         this.renderTouchView();
18355         
18356         this.touchViewEl.on('scroll', function(){
18357             this.el.dom.scrollTop = 0;
18358         }, this);
18359         
18360         this.originalValue = this.getValue();
18361         
18362         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18363         
18364         this.inputEl().on("click", this.showTouchView, this);
18365         if (this.triggerEl) {
18366             this.triggerEl.on("click", this.showTouchView, this);
18367         }
18368         
18369         
18370         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18371         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18372         
18373         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18374         
18375         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18376         this.store.on('load', this.onTouchViewLoad, this);
18377         this.store.on('loadexception', this.onTouchViewLoadException, this);
18378         
18379         if(this.hiddenName){
18380             
18381             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18382             
18383             this.hiddenField.dom.value =
18384                 this.hiddenValue !== undefined ? this.hiddenValue :
18385                 this.value !== undefined ? this.value : '';
18386         
18387             this.el.dom.removeAttribute('name');
18388             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18389         }
18390         
18391         if(this.multiple){
18392             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18393             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18394         }
18395         
18396         if(this.removable && !this.multiple){
18397             var close = this.closeTriggerEl();
18398             if(close){
18399                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18400                 close.on('click', this.removeBtnClick, this, close);
18401             }
18402         }
18403         /*
18404          * fix the bug in Safari iOS8
18405          */
18406         this.inputEl().on("focus", function(e){
18407             document.activeElement.blur();
18408         }, this);
18409         
18410         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18411         
18412         return;
18413         
18414         
18415     },
18416     
18417     renderTouchView : function()
18418     {
18419         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18420         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18421         
18422         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18423         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18424         
18425         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18426         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18427         this.touchViewBodyEl.setStyle('overflow', 'auto');
18428         
18429         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18430         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18431         
18432         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18433         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18434         
18435     },
18436     
18437     showTouchView : function()
18438     {
18439         if(this.disabled){
18440             return;
18441         }
18442         
18443         this.touchViewHeaderEl.hide();
18444
18445         if(this.modalTitle.length){
18446             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18447             this.touchViewHeaderEl.show();
18448         }
18449
18450         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18451         this.touchViewEl.show();
18452
18453         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18454         
18455         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18456         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18457
18458         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18459
18460         if(this.modalTitle.length){
18461             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18462         }
18463         
18464         this.touchViewBodyEl.setHeight(bodyHeight);
18465
18466         if(this.animate){
18467             var _this = this;
18468             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18469         }else{
18470             this.touchViewEl.addClass(['in','show']);
18471         }
18472         
18473         if(this._touchViewMask){
18474             Roo.get(document.body).addClass("x-body-masked");
18475             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18476             this._touchViewMask.setStyle('z-index', 10000);
18477             this._touchViewMask.addClass('show');
18478         }
18479         
18480         this.doTouchViewQuery();
18481         
18482     },
18483     
18484     hideTouchView : function()
18485     {
18486         this.touchViewEl.removeClass(['in','show']);
18487
18488         if(this.animate){
18489             var _this = this;
18490             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18491         }else{
18492             this.touchViewEl.setStyle('display', 'none');
18493         }
18494         
18495         if(this._touchViewMask){
18496             this._touchViewMask.removeClass('show');
18497             Roo.get(document.body).removeClass("x-body-masked");
18498         }
18499     },
18500     
18501     setTouchViewValue : function()
18502     {
18503         if(this.multiple){
18504             this.clearItem();
18505         
18506             var _this = this;
18507
18508             Roo.each(this.tickItems, function(o){
18509                 this.addItem(o);
18510             }, this);
18511         }
18512         
18513         this.hideTouchView();
18514     },
18515     
18516     doTouchViewQuery : function()
18517     {
18518         var qe = {
18519             query: '',
18520             forceAll: true,
18521             combo: this,
18522             cancel:false
18523         };
18524         
18525         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18526             return false;
18527         }
18528         
18529         if(!this.alwaysQuery || this.mode == 'local'){
18530             this.onTouchViewLoad();
18531             return;
18532         }
18533         
18534         this.store.load();
18535     },
18536     
18537     onTouchViewBeforeLoad : function(combo,opts)
18538     {
18539         return;
18540     },
18541
18542     // private
18543     onTouchViewLoad : function()
18544     {
18545         if(this.store.getCount() < 1){
18546             this.onTouchViewEmptyResults();
18547             return;
18548         }
18549         
18550         this.clearTouchView();
18551         
18552         var rawValue = this.getRawValue();
18553         
18554         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18555         
18556         this.tickItems = [];
18557         
18558         this.store.data.each(function(d, rowIndex){
18559             var row = this.touchViewListGroup.createChild(template);
18560             
18561             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18562                 row.addClass(d.data.cls);
18563             }
18564             
18565             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18566                 var cfg = {
18567                     data : d.data,
18568                     html : d.data[this.displayField]
18569                 };
18570                 
18571                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18572                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18573                 }
18574             }
18575             row.removeClass('selected');
18576             if(!this.multiple && this.valueField &&
18577                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18578             {
18579                 // radio buttons..
18580                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18581                 row.addClass('selected');
18582             }
18583             
18584             if(this.multiple && this.valueField &&
18585                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18586             {
18587                 
18588                 // checkboxes...
18589                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18590                 this.tickItems.push(d.data);
18591             }
18592             
18593             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18594             
18595         }, this);
18596         
18597         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18598         
18599         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18600
18601         if(this.modalTitle.length){
18602             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18603         }
18604
18605         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18606         
18607         if(this.mobile_restrict_height && listHeight < bodyHeight){
18608             this.touchViewBodyEl.setHeight(listHeight);
18609         }
18610         
18611         var _this = this;
18612         
18613         if(firstChecked && listHeight > bodyHeight){
18614             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18615         }
18616         
18617     },
18618     
18619     onTouchViewLoadException : function()
18620     {
18621         this.hideTouchView();
18622     },
18623     
18624     onTouchViewEmptyResults : function()
18625     {
18626         this.clearTouchView();
18627         
18628         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18629         
18630         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18631         
18632     },
18633     
18634     clearTouchView : function()
18635     {
18636         this.touchViewListGroup.dom.innerHTML = '';
18637     },
18638     
18639     onTouchViewClick : function(e, el, o)
18640     {
18641         e.preventDefault();
18642         
18643         var row = o.row;
18644         var rowIndex = o.rowIndex;
18645         
18646         var r = this.store.getAt(rowIndex);
18647         
18648         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18649             
18650             if(!this.multiple){
18651                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18652                     c.dom.removeAttribute('checked');
18653                 }, this);
18654
18655                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18656
18657                 this.setFromData(r.data);
18658
18659                 var close = this.closeTriggerEl();
18660
18661                 if(close){
18662                     close.show();
18663                 }
18664
18665                 this.hideTouchView();
18666
18667                 this.fireEvent('select', this, r, rowIndex);
18668
18669                 return;
18670             }
18671
18672             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18673                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18674                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18675                 return;
18676             }
18677
18678             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18679             this.addItem(r.data);
18680             this.tickItems.push(r.data);
18681         }
18682     },
18683     
18684     getAutoCreateNativeIOS : function()
18685     {
18686         var cfg = {
18687             cls: 'form-group' //input-group,
18688         };
18689         
18690         var combobox =  {
18691             tag: 'select',
18692             cls : 'roo-ios-select'
18693         };
18694         
18695         if (this.name) {
18696             combobox.name = this.name;
18697         }
18698         
18699         if (this.disabled) {
18700             combobox.disabled = true;
18701         }
18702         
18703         var settings = this;
18704         
18705         ['xs','sm','md','lg'].map(function(size){
18706             if (settings[size]) {
18707                 cfg.cls += ' col-' + size + '-' + settings[size];
18708             }
18709         });
18710         
18711         cfg.cn = combobox;
18712         
18713         return cfg;
18714         
18715     },
18716     
18717     initIOSView : function()
18718     {
18719         this.store.on('load', this.onIOSViewLoad, this);
18720         
18721         return;
18722     },
18723     
18724     onIOSViewLoad : function()
18725     {
18726         if(this.store.getCount() < 1){
18727             return;
18728         }
18729         
18730         this.clearIOSView();
18731         
18732         if(this.allowBlank) {
18733             
18734             var default_text = '-- SELECT --';
18735             
18736             if(this.placeholder.length){
18737                 default_text = this.placeholder;
18738             }
18739             
18740             if(this.emptyTitle.length){
18741                 default_text += ' - ' + this.emptyTitle + ' -';
18742             }
18743             
18744             var opt = this.inputEl().createChild({
18745                 tag: 'option',
18746                 value : 0,
18747                 html : default_text
18748             });
18749             
18750             var o = {};
18751             o[this.valueField] = 0;
18752             o[this.displayField] = default_text;
18753             
18754             this.ios_options.push({
18755                 data : o,
18756                 el : opt
18757             });
18758             
18759         }
18760         
18761         this.store.data.each(function(d, rowIndex){
18762             
18763             var html = '';
18764             
18765             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18766                 html = d.data[this.displayField];
18767             }
18768             
18769             var value = '';
18770             
18771             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18772                 value = d.data[this.valueField];
18773             }
18774             
18775             var option = {
18776                 tag: 'option',
18777                 value : value,
18778                 html : html
18779             };
18780             
18781             if(this.value == d.data[this.valueField]){
18782                 option['selected'] = true;
18783             }
18784             
18785             var opt = this.inputEl().createChild(option);
18786             
18787             this.ios_options.push({
18788                 data : d.data,
18789                 el : opt
18790             });
18791             
18792         }, this);
18793         
18794         this.inputEl().on('change', function(){
18795            this.fireEvent('select', this);
18796         }, this);
18797         
18798     },
18799     
18800     clearIOSView: function()
18801     {
18802         this.inputEl().dom.innerHTML = '';
18803         
18804         this.ios_options = [];
18805     },
18806     
18807     setIOSValue: function(v)
18808     {
18809         this.value = v;
18810         
18811         if(!this.ios_options){
18812             return;
18813         }
18814         
18815         Roo.each(this.ios_options, function(opts){
18816            
18817            opts.el.dom.removeAttribute('selected');
18818            
18819            if(opts.data[this.valueField] != v){
18820                return;
18821            }
18822            
18823            opts.el.dom.setAttribute('selected', true);
18824            
18825         }, this);
18826     }
18827
18828     /** 
18829     * @cfg {Boolean} grow 
18830     * @hide 
18831     */
18832     /** 
18833     * @cfg {Number} growMin 
18834     * @hide 
18835     */
18836     /** 
18837     * @cfg {Number} growMax 
18838     * @hide 
18839     */
18840     /**
18841      * @hide
18842      * @method autoSize
18843      */
18844 });
18845
18846 Roo.apply(Roo.bootstrap.ComboBox,  {
18847     
18848     header : {
18849         tag: 'div',
18850         cls: 'modal-header',
18851         cn: [
18852             {
18853                 tag: 'h4',
18854                 cls: 'modal-title'
18855             }
18856         ]
18857     },
18858     
18859     body : {
18860         tag: 'div',
18861         cls: 'modal-body',
18862         cn: [
18863             {
18864                 tag: 'ul',
18865                 cls: 'list-group'
18866             }
18867         ]
18868     },
18869     
18870     listItemRadio : {
18871         tag: 'li',
18872         cls: 'list-group-item',
18873         cn: [
18874             {
18875                 tag: 'span',
18876                 cls: 'roo-combobox-list-group-item-value'
18877             },
18878             {
18879                 tag: 'div',
18880                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18881                 cn: [
18882                     {
18883                         tag: 'input',
18884                         type: 'radio'
18885                     },
18886                     {
18887                         tag: 'label'
18888                     }
18889                 ]
18890             }
18891         ]
18892     },
18893     
18894     listItemCheckbox : {
18895         tag: 'li',
18896         cls: 'list-group-item',
18897         cn: [
18898             {
18899                 tag: 'span',
18900                 cls: 'roo-combobox-list-group-item-value'
18901             },
18902             {
18903                 tag: 'div',
18904                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18905                 cn: [
18906                     {
18907                         tag: 'input',
18908                         type: 'checkbox'
18909                     },
18910                     {
18911                         tag: 'label'
18912                     }
18913                 ]
18914             }
18915         ]
18916     },
18917     
18918     emptyResult : {
18919         tag: 'div',
18920         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18921     },
18922     
18923     footer : {
18924         tag: 'div',
18925         cls: 'modal-footer',
18926         cn: [
18927             {
18928                 tag: 'div',
18929                 cls: 'row',
18930                 cn: [
18931                     {
18932                         tag: 'div',
18933                         cls: 'col-xs-6 text-left',
18934                         cn: {
18935                             tag: 'button',
18936                             cls: 'btn btn-danger roo-touch-view-cancel',
18937                             html: 'Cancel'
18938                         }
18939                     },
18940                     {
18941                         tag: 'div',
18942                         cls: 'col-xs-6 text-right',
18943                         cn: {
18944                             tag: 'button',
18945                             cls: 'btn btn-success roo-touch-view-ok',
18946                             html: 'OK'
18947                         }
18948                     }
18949                 ]
18950             }
18951         ]
18952         
18953     }
18954 });
18955
18956 Roo.apply(Roo.bootstrap.ComboBox,  {
18957     
18958     touchViewTemplate : {
18959         tag: 'div',
18960         cls: 'modal fade roo-combobox-touch-view',
18961         cn: [
18962             {
18963                 tag: 'div',
18964                 cls: 'modal-dialog',
18965                 style : 'position:fixed', // we have to fix position....
18966                 cn: [
18967                     {
18968                         tag: 'div',
18969                         cls: 'modal-content',
18970                         cn: [
18971                             Roo.bootstrap.ComboBox.header,
18972                             Roo.bootstrap.ComboBox.body,
18973                             Roo.bootstrap.ComboBox.footer
18974                         ]
18975                     }
18976                 ]
18977             }
18978         ]
18979     }
18980 });/*
18981  * Based on:
18982  * Ext JS Library 1.1.1
18983  * Copyright(c) 2006-2007, Ext JS, LLC.
18984  *
18985  * Originally Released Under LGPL - original licence link has changed is not relivant.
18986  *
18987  * Fork - LGPL
18988  * <script type="text/javascript">
18989  */
18990
18991 /**
18992  * @class Roo.View
18993  * @extends Roo.util.Observable
18994  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18995  * This class also supports single and multi selection modes. <br>
18996  * Create a data model bound view:
18997  <pre><code>
18998  var store = new Roo.data.Store(...);
18999
19000  var view = new Roo.View({
19001     el : "my-element",
19002     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19003  
19004     singleSelect: true,
19005     selectedClass: "ydataview-selected",
19006     store: store
19007  });
19008
19009  // listen for node click?
19010  view.on("click", function(vw, index, node, e){
19011  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19012  });
19013
19014  // load XML data
19015  dataModel.load("foobar.xml");
19016  </code></pre>
19017  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19018  * <br><br>
19019  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19020  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19021  * 
19022  * Note: old style constructor is still suported (container, template, config)
19023  * 
19024  * @constructor
19025  * Create a new View
19026  * @param {Object} config The config object
19027  * 
19028  */
19029 Roo.View = function(config, depreciated_tpl, depreciated_config){
19030     
19031     this.parent = false;
19032     
19033     if (typeof(depreciated_tpl) == 'undefined') {
19034         // new way.. - universal constructor.
19035         Roo.apply(this, config);
19036         this.el  = Roo.get(this.el);
19037     } else {
19038         // old format..
19039         this.el  = Roo.get(config);
19040         this.tpl = depreciated_tpl;
19041         Roo.apply(this, depreciated_config);
19042     }
19043     this.wrapEl  = this.el.wrap().wrap();
19044     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19045     
19046     
19047     if(typeof(this.tpl) == "string"){
19048         this.tpl = new Roo.Template(this.tpl);
19049     } else {
19050         // support xtype ctors..
19051         this.tpl = new Roo.factory(this.tpl, Roo);
19052     }
19053     
19054     
19055     this.tpl.compile();
19056     
19057     /** @private */
19058     this.addEvents({
19059         /**
19060          * @event beforeclick
19061          * Fires before a click is processed. Returns false to cancel the default action.
19062          * @param {Roo.View} this
19063          * @param {Number} index The index of the target node
19064          * @param {HTMLElement} node The target node
19065          * @param {Roo.EventObject} e The raw event object
19066          */
19067             "beforeclick" : true,
19068         /**
19069          * @event click
19070          * Fires when a template node is clicked.
19071          * @param {Roo.View} this
19072          * @param {Number} index The index of the target node
19073          * @param {HTMLElement} node The target node
19074          * @param {Roo.EventObject} e The raw event object
19075          */
19076             "click" : true,
19077         /**
19078          * @event dblclick
19079          * Fires when a template node is double clicked.
19080          * @param {Roo.View} this
19081          * @param {Number} index The index of the target node
19082          * @param {HTMLElement} node The target node
19083          * @param {Roo.EventObject} e The raw event object
19084          */
19085             "dblclick" : true,
19086         /**
19087          * @event contextmenu
19088          * Fires when a template node is right clicked.
19089          * @param {Roo.View} this
19090          * @param {Number} index The index of the target node
19091          * @param {HTMLElement} node The target node
19092          * @param {Roo.EventObject} e The raw event object
19093          */
19094             "contextmenu" : true,
19095         /**
19096          * @event selectionchange
19097          * Fires when the selected nodes change.
19098          * @param {Roo.View} this
19099          * @param {Array} selections Array of the selected nodes
19100          */
19101             "selectionchange" : true,
19102     
19103         /**
19104          * @event beforeselect
19105          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19106          * @param {Roo.View} this
19107          * @param {HTMLElement} node The node to be selected
19108          * @param {Array} selections Array of currently selected nodes
19109          */
19110             "beforeselect" : true,
19111         /**
19112          * @event preparedata
19113          * Fires on every row to render, to allow you to change the data.
19114          * @param {Roo.View} this
19115          * @param {Object} data to be rendered (change this)
19116          */
19117           "preparedata" : true
19118           
19119           
19120         });
19121
19122
19123
19124     this.el.on({
19125         "click": this.onClick,
19126         "dblclick": this.onDblClick,
19127         "contextmenu": this.onContextMenu,
19128         scope:this
19129     });
19130
19131     this.selections = [];
19132     this.nodes = [];
19133     this.cmp = new Roo.CompositeElementLite([]);
19134     if(this.store){
19135         this.store = Roo.factory(this.store, Roo.data);
19136         this.setStore(this.store, true);
19137     }
19138     
19139     if ( this.footer && this.footer.xtype) {
19140            
19141          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19142         
19143         this.footer.dataSource = this.store;
19144         this.footer.container = fctr;
19145         this.footer = Roo.factory(this.footer, Roo);
19146         fctr.insertFirst(this.el);
19147         
19148         // this is a bit insane - as the paging toolbar seems to detach the el..
19149 //        dom.parentNode.parentNode.parentNode
19150          // they get detached?
19151     }
19152     
19153     
19154     Roo.View.superclass.constructor.call(this);
19155     
19156     
19157 };
19158
19159 Roo.extend(Roo.View, Roo.util.Observable, {
19160     
19161      /**
19162      * @cfg {Roo.data.Store} store Data store to load data from.
19163      */
19164     store : false,
19165     
19166     /**
19167      * @cfg {String|Roo.Element} el The container element.
19168      */
19169     el : '',
19170     
19171     /**
19172      * @cfg {String|Roo.Template} tpl The template used by this View 
19173      */
19174     tpl : false,
19175     /**
19176      * @cfg {String} dataName the named area of the template to use as the data area
19177      *                          Works with domtemplates roo-name="name"
19178      */
19179     dataName: false,
19180     /**
19181      * @cfg {String} selectedClass The css class to add to selected nodes
19182      */
19183     selectedClass : "x-view-selected",
19184      /**
19185      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19186      */
19187     emptyText : "",
19188     
19189     /**
19190      * @cfg {String} text to display on mask (default Loading)
19191      */
19192     mask : false,
19193     /**
19194      * @cfg {Boolean} multiSelect Allow multiple selection
19195      */
19196     multiSelect : false,
19197     /**
19198      * @cfg {Boolean} singleSelect Allow single selection
19199      */
19200     singleSelect:  false,
19201     
19202     /**
19203      * @cfg {Boolean} toggleSelect - selecting 
19204      */
19205     toggleSelect : false,
19206     
19207     /**
19208      * @cfg {Boolean} tickable - selecting 
19209      */
19210     tickable : false,
19211     
19212     /**
19213      * Returns the element this view is bound to.
19214      * @return {Roo.Element}
19215      */
19216     getEl : function(){
19217         return this.wrapEl;
19218     },
19219     
19220     
19221
19222     /**
19223      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19224      */
19225     refresh : function(){
19226         //Roo.log('refresh');
19227         var t = this.tpl;
19228         
19229         // if we are using something like 'domtemplate', then
19230         // the what gets used is:
19231         // t.applySubtemplate(NAME, data, wrapping data..)
19232         // the outer template then get' applied with
19233         //     the store 'extra data'
19234         // and the body get's added to the
19235         //      roo-name="data" node?
19236         //      <span class='roo-tpl-{name}'></span> ?????
19237         
19238         
19239         
19240         this.clearSelections();
19241         this.el.update("");
19242         var html = [];
19243         var records = this.store.getRange();
19244         if(records.length < 1) {
19245             
19246             // is this valid??  = should it render a template??
19247             
19248             this.el.update(this.emptyText);
19249             return;
19250         }
19251         var el = this.el;
19252         if (this.dataName) {
19253             this.el.update(t.apply(this.store.meta)); //????
19254             el = this.el.child('.roo-tpl-' + this.dataName);
19255         }
19256         
19257         for(var i = 0, len = records.length; i < len; i++){
19258             var data = this.prepareData(records[i].data, i, records[i]);
19259             this.fireEvent("preparedata", this, data, i, records[i]);
19260             
19261             var d = Roo.apply({}, data);
19262             
19263             if(this.tickable){
19264                 Roo.apply(d, {'roo-id' : Roo.id()});
19265                 
19266                 var _this = this;
19267             
19268                 Roo.each(this.parent.item, function(item){
19269                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19270                         return;
19271                     }
19272                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19273                 });
19274             }
19275             
19276             html[html.length] = Roo.util.Format.trim(
19277                 this.dataName ?
19278                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19279                     t.apply(d)
19280             );
19281         }
19282         
19283         
19284         
19285         el.update(html.join(""));
19286         this.nodes = el.dom.childNodes;
19287         this.updateIndexes(0);
19288     },
19289     
19290
19291     /**
19292      * Function to override to reformat the data that is sent to
19293      * the template for each node.
19294      * DEPRICATED - use the preparedata event handler.
19295      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19296      * a JSON object for an UpdateManager bound view).
19297      */
19298     prepareData : function(data, index, record)
19299     {
19300         this.fireEvent("preparedata", this, data, index, record);
19301         return data;
19302     },
19303
19304     onUpdate : function(ds, record){
19305         // Roo.log('on update');   
19306         this.clearSelections();
19307         var index = this.store.indexOf(record);
19308         var n = this.nodes[index];
19309         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19310         n.parentNode.removeChild(n);
19311         this.updateIndexes(index, index);
19312     },
19313
19314     
19315     
19316 // --------- FIXME     
19317     onAdd : function(ds, records, index)
19318     {
19319         //Roo.log(['on Add', ds, records, index] );        
19320         this.clearSelections();
19321         if(this.nodes.length == 0){
19322             this.refresh();
19323             return;
19324         }
19325         var n = this.nodes[index];
19326         for(var i = 0, len = records.length; i < len; i++){
19327             var d = this.prepareData(records[i].data, i, records[i]);
19328             if(n){
19329                 this.tpl.insertBefore(n, d);
19330             }else{
19331                 
19332                 this.tpl.append(this.el, d);
19333             }
19334         }
19335         this.updateIndexes(index);
19336     },
19337
19338     onRemove : function(ds, record, index){
19339        // Roo.log('onRemove');
19340         this.clearSelections();
19341         var el = this.dataName  ?
19342             this.el.child('.roo-tpl-' + this.dataName) :
19343             this.el; 
19344         
19345         el.dom.removeChild(this.nodes[index]);
19346         this.updateIndexes(index);
19347     },
19348
19349     /**
19350      * Refresh an individual node.
19351      * @param {Number} index
19352      */
19353     refreshNode : function(index){
19354         this.onUpdate(this.store, this.store.getAt(index));
19355     },
19356
19357     updateIndexes : function(startIndex, endIndex){
19358         var ns = this.nodes;
19359         startIndex = startIndex || 0;
19360         endIndex = endIndex || ns.length - 1;
19361         for(var i = startIndex; i <= endIndex; i++){
19362             ns[i].nodeIndex = i;
19363         }
19364     },
19365
19366     /**
19367      * Changes the data store this view uses and refresh the view.
19368      * @param {Store} store
19369      */
19370     setStore : function(store, initial){
19371         if(!initial && this.store){
19372             this.store.un("datachanged", this.refresh);
19373             this.store.un("add", this.onAdd);
19374             this.store.un("remove", this.onRemove);
19375             this.store.un("update", this.onUpdate);
19376             this.store.un("clear", this.refresh);
19377             this.store.un("beforeload", this.onBeforeLoad);
19378             this.store.un("load", this.onLoad);
19379             this.store.un("loadexception", this.onLoad);
19380         }
19381         if(store){
19382           
19383             store.on("datachanged", this.refresh, this);
19384             store.on("add", this.onAdd, this);
19385             store.on("remove", this.onRemove, this);
19386             store.on("update", this.onUpdate, this);
19387             store.on("clear", this.refresh, this);
19388             store.on("beforeload", this.onBeforeLoad, this);
19389             store.on("load", this.onLoad, this);
19390             store.on("loadexception", this.onLoad, this);
19391         }
19392         
19393         if(store){
19394             this.refresh();
19395         }
19396     },
19397     /**
19398      * onbeforeLoad - masks the loading area.
19399      *
19400      */
19401     onBeforeLoad : function(store,opts)
19402     {
19403          //Roo.log('onBeforeLoad');   
19404         if (!opts.add) {
19405             this.el.update("");
19406         }
19407         this.el.mask(this.mask ? this.mask : "Loading" ); 
19408     },
19409     onLoad : function ()
19410     {
19411         this.el.unmask();
19412     },
19413     
19414
19415     /**
19416      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19417      * @param {HTMLElement} node
19418      * @return {HTMLElement} The template node
19419      */
19420     findItemFromChild : function(node){
19421         var el = this.dataName  ?
19422             this.el.child('.roo-tpl-' + this.dataName,true) :
19423             this.el.dom; 
19424         
19425         if(!node || node.parentNode == el){
19426                     return node;
19427             }
19428             var p = node.parentNode;
19429             while(p && p != el){
19430             if(p.parentNode == el){
19431                 return p;
19432             }
19433             p = p.parentNode;
19434         }
19435             return null;
19436     },
19437
19438     /** @ignore */
19439     onClick : function(e){
19440         var item = this.findItemFromChild(e.getTarget());
19441         if(item){
19442             var index = this.indexOf(item);
19443             if(this.onItemClick(item, index, e) !== false){
19444                 this.fireEvent("click", this, index, item, e);
19445             }
19446         }else{
19447             this.clearSelections();
19448         }
19449     },
19450
19451     /** @ignore */
19452     onContextMenu : function(e){
19453         var item = this.findItemFromChild(e.getTarget());
19454         if(item){
19455             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19456         }
19457     },
19458
19459     /** @ignore */
19460     onDblClick : function(e){
19461         var item = this.findItemFromChild(e.getTarget());
19462         if(item){
19463             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19464         }
19465     },
19466
19467     onItemClick : function(item, index, e)
19468     {
19469         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19470             return false;
19471         }
19472         if (this.toggleSelect) {
19473             var m = this.isSelected(item) ? 'unselect' : 'select';
19474             //Roo.log(m);
19475             var _t = this;
19476             _t[m](item, true, false);
19477             return true;
19478         }
19479         if(this.multiSelect || this.singleSelect){
19480             if(this.multiSelect && e.shiftKey && this.lastSelection){
19481                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19482             }else{
19483                 this.select(item, this.multiSelect && e.ctrlKey);
19484                 this.lastSelection = item;
19485             }
19486             
19487             if(!this.tickable){
19488                 e.preventDefault();
19489             }
19490             
19491         }
19492         return true;
19493     },
19494
19495     /**
19496      * Get the number of selected nodes.
19497      * @return {Number}
19498      */
19499     getSelectionCount : function(){
19500         return this.selections.length;
19501     },
19502
19503     /**
19504      * Get the currently selected nodes.
19505      * @return {Array} An array of HTMLElements
19506      */
19507     getSelectedNodes : function(){
19508         return this.selections;
19509     },
19510
19511     /**
19512      * Get the indexes of the selected nodes.
19513      * @return {Array}
19514      */
19515     getSelectedIndexes : function(){
19516         var indexes = [], s = this.selections;
19517         for(var i = 0, len = s.length; i < len; i++){
19518             indexes.push(s[i].nodeIndex);
19519         }
19520         return indexes;
19521     },
19522
19523     /**
19524      * Clear all selections
19525      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19526      */
19527     clearSelections : function(suppressEvent){
19528         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19529             this.cmp.elements = this.selections;
19530             this.cmp.removeClass(this.selectedClass);
19531             this.selections = [];
19532             if(!suppressEvent){
19533                 this.fireEvent("selectionchange", this, this.selections);
19534             }
19535         }
19536     },
19537
19538     /**
19539      * Returns true if the passed node is selected
19540      * @param {HTMLElement/Number} node The node or node index
19541      * @return {Boolean}
19542      */
19543     isSelected : function(node){
19544         var s = this.selections;
19545         if(s.length < 1){
19546             return false;
19547         }
19548         node = this.getNode(node);
19549         return s.indexOf(node) !== -1;
19550     },
19551
19552     /**
19553      * Selects nodes.
19554      * @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
19555      * @param {Boolean} keepExisting (optional) true to keep existing selections
19556      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19557      */
19558     select : function(nodeInfo, keepExisting, suppressEvent){
19559         if(nodeInfo instanceof Array){
19560             if(!keepExisting){
19561                 this.clearSelections(true);
19562             }
19563             for(var i = 0, len = nodeInfo.length; i < len; i++){
19564                 this.select(nodeInfo[i], true, true);
19565             }
19566             return;
19567         } 
19568         var node = this.getNode(nodeInfo);
19569         if(!node || this.isSelected(node)){
19570             return; // already selected.
19571         }
19572         if(!keepExisting){
19573             this.clearSelections(true);
19574         }
19575         
19576         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19577             Roo.fly(node).addClass(this.selectedClass);
19578             this.selections.push(node);
19579             if(!suppressEvent){
19580                 this.fireEvent("selectionchange", this, this.selections);
19581             }
19582         }
19583         
19584         
19585     },
19586       /**
19587      * Unselects nodes.
19588      * @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
19589      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19590      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19591      */
19592     unselect : function(nodeInfo, keepExisting, suppressEvent)
19593     {
19594         if(nodeInfo instanceof Array){
19595             Roo.each(this.selections, function(s) {
19596                 this.unselect(s, nodeInfo);
19597             }, this);
19598             return;
19599         }
19600         var node = this.getNode(nodeInfo);
19601         if(!node || !this.isSelected(node)){
19602             //Roo.log("not selected");
19603             return; // not selected.
19604         }
19605         // fireevent???
19606         var ns = [];
19607         Roo.each(this.selections, function(s) {
19608             if (s == node ) {
19609                 Roo.fly(node).removeClass(this.selectedClass);
19610
19611                 return;
19612             }
19613             ns.push(s);
19614         },this);
19615         
19616         this.selections= ns;
19617         this.fireEvent("selectionchange", this, this.selections);
19618     },
19619
19620     /**
19621      * Gets a template node.
19622      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19623      * @return {HTMLElement} The node or null if it wasn't found
19624      */
19625     getNode : function(nodeInfo){
19626         if(typeof nodeInfo == "string"){
19627             return document.getElementById(nodeInfo);
19628         }else if(typeof nodeInfo == "number"){
19629             return this.nodes[nodeInfo];
19630         }
19631         return nodeInfo;
19632     },
19633
19634     /**
19635      * Gets a range template nodes.
19636      * @param {Number} startIndex
19637      * @param {Number} endIndex
19638      * @return {Array} An array of nodes
19639      */
19640     getNodes : function(start, end){
19641         var ns = this.nodes;
19642         start = start || 0;
19643         end = typeof end == "undefined" ? ns.length - 1 : end;
19644         var nodes = [];
19645         if(start <= end){
19646             for(var i = start; i <= end; i++){
19647                 nodes.push(ns[i]);
19648             }
19649         } else{
19650             for(var i = start; i >= end; i--){
19651                 nodes.push(ns[i]);
19652             }
19653         }
19654         return nodes;
19655     },
19656
19657     /**
19658      * Finds the index of the passed node
19659      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19660      * @return {Number} The index of the node or -1
19661      */
19662     indexOf : function(node){
19663         node = this.getNode(node);
19664         if(typeof node.nodeIndex == "number"){
19665             return node.nodeIndex;
19666         }
19667         var ns = this.nodes;
19668         for(var i = 0, len = ns.length; i < len; i++){
19669             if(ns[i] == node){
19670                 return i;
19671             }
19672         }
19673         return -1;
19674     }
19675 });
19676 /*
19677  * - LGPL
19678  *
19679  * based on jquery fullcalendar
19680  * 
19681  */
19682
19683 Roo.bootstrap = Roo.bootstrap || {};
19684 /**
19685  * @class Roo.bootstrap.Calendar
19686  * @extends Roo.bootstrap.Component
19687  * Bootstrap Calendar class
19688  * @cfg {Boolean} loadMask (true|false) default false
19689  * @cfg {Object} header generate the user specific header of the calendar, default false
19690
19691  * @constructor
19692  * Create a new Container
19693  * @param {Object} config The config object
19694  */
19695
19696
19697
19698 Roo.bootstrap.Calendar = function(config){
19699     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19700      this.addEvents({
19701         /**
19702              * @event select
19703              * Fires when a date is selected
19704              * @param {DatePicker} this
19705              * @param {Date} date The selected date
19706              */
19707         'select': true,
19708         /**
19709              * @event monthchange
19710              * Fires when the displayed month changes 
19711              * @param {DatePicker} this
19712              * @param {Date} date The selected month
19713              */
19714         'monthchange': true,
19715         /**
19716              * @event evententer
19717              * Fires when mouse over an event
19718              * @param {Calendar} this
19719              * @param {event} Event
19720              */
19721         'evententer': true,
19722         /**
19723              * @event eventleave
19724              * Fires when the mouse leaves an
19725              * @param {Calendar} this
19726              * @param {event}
19727              */
19728         'eventleave': true,
19729         /**
19730              * @event eventclick
19731              * Fires when the mouse click an
19732              * @param {Calendar} this
19733              * @param {event}
19734              */
19735         'eventclick': true
19736         
19737     });
19738
19739 };
19740
19741 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19742     
19743      /**
19744      * @cfg {Number} startDay
19745      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19746      */
19747     startDay : 0,
19748     
19749     loadMask : false,
19750     
19751     header : false,
19752       
19753     getAutoCreate : function(){
19754         
19755         
19756         var fc_button = function(name, corner, style, content ) {
19757             return Roo.apply({},{
19758                 tag : 'span',
19759                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19760                          (corner.length ?
19761                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19762                             ''
19763                         ),
19764                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19765                 unselectable: 'on'
19766             });
19767         };
19768         
19769         var header = {};
19770         
19771         if(!this.header){
19772             header = {
19773                 tag : 'table',
19774                 cls : 'fc-header',
19775                 style : 'width:100%',
19776                 cn : [
19777                     {
19778                         tag: 'tr',
19779                         cn : [
19780                             {
19781                                 tag : 'td',
19782                                 cls : 'fc-header-left',
19783                                 cn : [
19784                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19785                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19786                                     { tag: 'span', cls: 'fc-header-space' },
19787                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19788
19789
19790                                 ]
19791                             },
19792
19793                             {
19794                                 tag : 'td',
19795                                 cls : 'fc-header-center',
19796                                 cn : [
19797                                     {
19798                                         tag: 'span',
19799                                         cls: 'fc-header-title',
19800                                         cn : {
19801                                             tag: 'H2',
19802                                             html : 'month / year'
19803                                         }
19804                                     }
19805
19806                                 ]
19807                             },
19808                             {
19809                                 tag : 'td',
19810                                 cls : 'fc-header-right',
19811                                 cn : [
19812                               /*      fc_button('month', 'left', '', 'month' ),
19813                                     fc_button('week', '', '', 'week' ),
19814                                     fc_button('day', 'right', '', 'day' )
19815                                 */    
19816
19817                                 ]
19818                             }
19819
19820                         ]
19821                     }
19822                 ]
19823             };
19824         }
19825         
19826         header = this.header;
19827         
19828        
19829         var cal_heads = function() {
19830             var ret = [];
19831             // fixme - handle this.
19832             
19833             for (var i =0; i < Date.dayNames.length; i++) {
19834                 var d = Date.dayNames[i];
19835                 ret.push({
19836                     tag: 'th',
19837                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19838                     html : d.substring(0,3)
19839                 });
19840                 
19841             }
19842             ret[0].cls += ' fc-first';
19843             ret[6].cls += ' fc-last';
19844             return ret;
19845         };
19846         var cal_cell = function(n) {
19847             return  {
19848                 tag: 'td',
19849                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19850                 cn : [
19851                     {
19852                         cn : [
19853                             {
19854                                 cls: 'fc-day-number',
19855                                 html: 'D'
19856                             },
19857                             {
19858                                 cls: 'fc-day-content',
19859                              
19860                                 cn : [
19861                                      {
19862                                         style: 'position: relative;' // height: 17px;
19863                                     }
19864                                 ]
19865                             }
19866                             
19867                             
19868                         ]
19869                     }
19870                 ]
19871                 
19872             }
19873         };
19874         var cal_rows = function() {
19875             
19876             var ret = [];
19877             for (var r = 0; r < 6; r++) {
19878                 var row= {
19879                     tag : 'tr',
19880                     cls : 'fc-week',
19881                     cn : []
19882                 };
19883                 
19884                 for (var i =0; i < Date.dayNames.length; i++) {
19885                     var d = Date.dayNames[i];
19886                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19887
19888                 }
19889                 row.cn[0].cls+=' fc-first';
19890                 row.cn[0].cn[0].style = 'min-height:90px';
19891                 row.cn[6].cls+=' fc-last';
19892                 ret.push(row);
19893                 
19894             }
19895             ret[0].cls += ' fc-first';
19896             ret[4].cls += ' fc-prev-last';
19897             ret[5].cls += ' fc-last';
19898             return ret;
19899             
19900         };
19901         
19902         var cal_table = {
19903             tag: 'table',
19904             cls: 'fc-border-separate',
19905             style : 'width:100%',
19906             cellspacing  : 0,
19907             cn : [
19908                 { 
19909                     tag: 'thead',
19910                     cn : [
19911                         { 
19912                             tag: 'tr',
19913                             cls : 'fc-first fc-last',
19914                             cn : cal_heads()
19915                         }
19916                     ]
19917                 },
19918                 { 
19919                     tag: 'tbody',
19920                     cn : cal_rows()
19921                 }
19922                   
19923             ]
19924         };
19925          
19926          var cfg = {
19927             cls : 'fc fc-ltr',
19928             cn : [
19929                 header,
19930                 {
19931                     cls : 'fc-content',
19932                     style : "position: relative;",
19933                     cn : [
19934                         {
19935                             cls : 'fc-view fc-view-month fc-grid',
19936                             style : 'position: relative',
19937                             unselectable : 'on',
19938                             cn : [
19939                                 {
19940                                     cls : 'fc-event-container',
19941                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19942                                 },
19943                                 cal_table
19944                             ]
19945                         }
19946                     ]
19947     
19948                 }
19949            ] 
19950             
19951         };
19952         
19953          
19954         
19955         return cfg;
19956     },
19957     
19958     
19959     initEvents : function()
19960     {
19961         if(!this.store){
19962             throw "can not find store for calendar";
19963         }
19964         
19965         var mark = {
19966             tag: "div",
19967             cls:"x-dlg-mask",
19968             style: "text-align:center",
19969             cn: [
19970                 {
19971                     tag: "div",
19972                     style: "background-color:white;width:50%;margin:250 auto",
19973                     cn: [
19974                         {
19975                             tag: "img",
19976                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19977                         },
19978                         {
19979                             tag: "span",
19980                             html: "Loading"
19981                         }
19982                         
19983                     ]
19984                 }
19985             ]
19986         };
19987         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19988         
19989         var size = this.el.select('.fc-content', true).first().getSize();
19990         this.maskEl.setSize(size.width, size.height);
19991         this.maskEl.enableDisplayMode("block");
19992         if(!this.loadMask){
19993             this.maskEl.hide();
19994         }
19995         
19996         this.store = Roo.factory(this.store, Roo.data);
19997         this.store.on('load', this.onLoad, this);
19998         this.store.on('beforeload', this.onBeforeLoad, this);
19999         
20000         this.resize();
20001         
20002         this.cells = this.el.select('.fc-day',true);
20003         //Roo.log(this.cells);
20004         this.textNodes = this.el.query('.fc-day-number');
20005         this.cells.addClassOnOver('fc-state-hover');
20006         
20007         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20008         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20009         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20010         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20011         
20012         this.on('monthchange', this.onMonthChange, this);
20013         
20014         this.update(new Date().clearTime());
20015     },
20016     
20017     resize : function() {
20018         var sz  = this.el.getSize();
20019         
20020         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20021         this.el.select('.fc-day-content div',true).setHeight(34);
20022     },
20023     
20024     
20025     // private
20026     showPrevMonth : function(e){
20027         this.update(this.activeDate.add("mo", -1));
20028     },
20029     showToday : function(e){
20030         this.update(new Date().clearTime());
20031     },
20032     // private
20033     showNextMonth : function(e){
20034         this.update(this.activeDate.add("mo", 1));
20035     },
20036
20037     // private
20038     showPrevYear : function(){
20039         this.update(this.activeDate.add("y", -1));
20040     },
20041
20042     // private
20043     showNextYear : function(){
20044         this.update(this.activeDate.add("y", 1));
20045     },
20046
20047     
20048    // private
20049     update : function(date)
20050     {
20051         var vd = this.activeDate;
20052         this.activeDate = date;
20053 //        if(vd && this.el){
20054 //            var t = date.getTime();
20055 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20056 //                Roo.log('using add remove');
20057 //                
20058 //                this.fireEvent('monthchange', this, date);
20059 //                
20060 //                this.cells.removeClass("fc-state-highlight");
20061 //                this.cells.each(function(c){
20062 //                   if(c.dateValue == t){
20063 //                       c.addClass("fc-state-highlight");
20064 //                       setTimeout(function(){
20065 //                            try{c.dom.firstChild.focus();}catch(e){}
20066 //                       }, 50);
20067 //                       return false;
20068 //                   }
20069 //                   return true;
20070 //                });
20071 //                return;
20072 //            }
20073 //        }
20074         
20075         var days = date.getDaysInMonth();
20076         
20077         var firstOfMonth = date.getFirstDateOfMonth();
20078         var startingPos = firstOfMonth.getDay()-this.startDay;
20079         
20080         if(startingPos < this.startDay){
20081             startingPos += 7;
20082         }
20083         
20084         var pm = date.add(Date.MONTH, -1);
20085         var prevStart = pm.getDaysInMonth()-startingPos;
20086 //        
20087         this.cells = this.el.select('.fc-day',true);
20088         this.textNodes = this.el.query('.fc-day-number');
20089         this.cells.addClassOnOver('fc-state-hover');
20090         
20091         var cells = this.cells.elements;
20092         var textEls = this.textNodes;
20093         
20094         Roo.each(cells, function(cell){
20095             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20096         });
20097         
20098         days += startingPos;
20099
20100         // convert everything to numbers so it's fast
20101         var day = 86400000;
20102         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20103         //Roo.log(d);
20104         //Roo.log(pm);
20105         //Roo.log(prevStart);
20106         
20107         var today = new Date().clearTime().getTime();
20108         var sel = date.clearTime().getTime();
20109         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20110         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20111         var ddMatch = this.disabledDatesRE;
20112         var ddText = this.disabledDatesText;
20113         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20114         var ddaysText = this.disabledDaysText;
20115         var format = this.format;
20116         
20117         var setCellClass = function(cal, cell){
20118             cell.row = 0;
20119             cell.events = [];
20120             cell.more = [];
20121             //Roo.log('set Cell Class');
20122             cell.title = "";
20123             var t = d.getTime();
20124             
20125             //Roo.log(d);
20126             
20127             cell.dateValue = t;
20128             if(t == today){
20129                 cell.className += " fc-today";
20130                 cell.className += " fc-state-highlight";
20131                 cell.title = cal.todayText;
20132             }
20133             if(t == sel){
20134                 // disable highlight in other month..
20135                 //cell.className += " fc-state-highlight";
20136                 
20137             }
20138             // disabling
20139             if(t < min) {
20140                 cell.className = " fc-state-disabled";
20141                 cell.title = cal.minText;
20142                 return;
20143             }
20144             if(t > max) {
20145                 cell.className = " fc-state-disabled";
20146                 cell.title = cal.maxText;
20147                 return;
20148             }
20149             if(ddays){
20150                 if(ddays.indexOf(d.getDay()) != -1){
20151                     cell.title = ddaysText;
20152                     cell.className = " fc-state-disabled";
20153                 }
20154             }
20155             if(ddMatch && format){
20156                 var fvalue = d.dateFormat(format);
20157                 if(ddMatch.test(fvalue)){
20158                     cell.title = ddText.replace("%0", fvalue);
20159                     cell.className = " fc-state-disabled";
20160                 }
20161             }
20162             
20163             if (!cell.initialClassName) {
20164                 cell.initialClassName = cell.dom.className;
20165             }
20166             
20167             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20168         };
20169
20170         var i = 0;
20171         
20172         for(; i < startingPos; i++) {
20173             textEls[i].innerHTML = (++prevStart);
20174             d.setDate(d.getDate()+1);
20175             
20176             cells[i].className = "fc-past fc-other-month";
20177             setCellClass(this, cells[i]);
20178         }
20179         
20180         var intDay = 0;
20181         
20182         for(; i < days; i++){
20183             intDay = i - startingPos + 1;
20184             textEls[i].innerHTML = (intDay);
20185             d.setDate(d.getDate()+1);
20186             
20187             cells[i].className = ''; // "x-date-active";
20188             setCellClass(this, cells[i]);
20189         }
20190         var extraDays = 0;
20191         
20192         for(; i < 42; i++) {
20193             textEls[i].innerHTML = (++extraDays);
20194             d.setDate(d.getDate()+1);
20195             
20196             cells[i].className = "fc-future fc-other-month";
20197             setCellClass(this, cells[i]);
20198         }
20199         
20200         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20201         
20202         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20203         
20204         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20205         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20206         
20207         if(totalRows != 6){
20208             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20209             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20210         }
20211         
20212         this.fireEvent('monthchange', this, date);
20213         
20214         
20215         /*
20216         if(!this.internalRender){
20217             var main = this.el.dom.firstChild;
20218             var w = main.offsetWidth;
20219             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20220             Roo.fly(main).setWidth(w);
20221             this.internalRender = true;
20222             // opera does not respect the auto grow header center column
20223             // then, after it gets a width opera refuses to recalculate
20224             // without a second pass
20225             if(Roo.isOpera && !this.secondPass){
20226                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20227                 this.secondPass = true;
20228                 this.update.defer(10, this, [date]);
20229             }
20230         }
20231         */
20232         
20233     },
20234     
20235     findCell : function(dt) {
20236         dt = dt.clearTime().getTime();
20237         var ret = false;
20238         this.cells.each(function(c){
20239             //Roo.log("check " +c.dateValue + '?=' + dt);
20240             if(c.dateValue == dt){
20241                 ret = c;
20242                 return false;
20243             }
20244             return true;
20245         });
20246         
20247         return ret;
20248     },
20249     
20250     findCells : function(ev) {
20251         var s = ev.start.clone().clearTime().getTime();
20252        // Roo.log(s);
20253         var e= ev.end.clone().clearTime().getTime();
20254        // Roo.log(e);
20255         var ret = [];
20256         this.cells.each(function(c){
20257              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20258             
20259             if(c.dateValue > e){
20260                 return ;
20261             }
20262             if(c.dateValue < s){
20263                 return ;
20264             }
20265             ret.push(c);
20266         });
20267         
20268         return ret;    
20269     },
20270     
20271 //    findBestRow: function(cells)
20272 //    {
20273 //        var ret = 0;
20274 //        
20275 //        for (var i =0 ; i < cells.length;i++) {
20276 //            ret  = Math.max(cells[i].rows || 0,ret);
20277 //        }
20278 //        return ret;
20279 //        
20280 //    },
20281     
20282     
20283     addItem : function(ev)
20284     {
20285         // look for vertical location slot in
20286         var cells = this.findCells(ev);
20287         
20288 //        ev.row = this.findBestRow(cells);
20289         
20290         // work out the location.
20291         
20292         var crow = false;
20293         var rows = [];
20294         for(var i =0; i < cells.length; i++) {
20295             
20296             cells[i].row = cells[0].row;
20297             
20298             if(i == 0){
20299                 cells[i].row = cells[i].row + 1;
20300             }
20301             
20302             if (!crow) {
20303                 crow = {
20304                     start : cells[i],
20305                     end :  cells[i]
20306                 };
20307                 continue;
20308             }
20309             if (crow.start.getY() == cells[i].getY()) {
20310                 // on same row.
20311                 crow.end = cells[i];
20312                 continue;
20313             }
20314             // different row.
20315             rows.push(crow);
20316             crow = {
20317                 start: cells[i],
20318                 end : cells[i]
20319             };
20320             
20321         }
20322         
20323         rows.push(crow);
20324         ev.els = [];
20325         ev.rows = rows;
20326         ev.cells = cells;
20327         
20328         cells[0].events.push(ev);
20329         
20330         this.calevents.push(ev);
20331     },
20332     
20333     clearEvents: function() {
20334         
20335         if(!this.calevents){
20336             return;
20337         }
20338         
20339         Roo.each(this.cells.elements, function(c){
20340             c.row = 0;
20341             c.events = [];
20342             c.more = [];
20343         });
20344         
20345         Roo.each(this.calevents, function(e) {
20346             Roo.each(e.els, function(el) {
20347                 el.un('mouseenter' ,this.onEventEnter, this);
20348                 el.un('mouseleave' ,this.onEventLeave, this);
20349                 el.remove();
20350             },this);
20351         },this);
20352         
20353         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20354             e.remove();
20355         });
20356         
20357     },
20358     
20359     renderEvents: function()
20360     {   
20361         var _this = this;
20362         
20363         this.cells.each(function(c) {
20364             
20365             if(c.row < 5){
20366                 return;
20367             }
20368             
20369             var ev = c.events;
20370             
20371             var r = 4;
20372             if(c.row != c.events.length){
20373                 r = 4 - (4 - (c.row - c.events.length));
20374             }
20375             
20376             c.events = ev.slice(0, r);
20377             c.more = ev.slice(r);
20378             
20379             if(c.more.length && c.more.length == 1){
20380                 c.events.push(c.more.pop());
20381             }
20382             
20383             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20384             
20385         });
20386             
20387         this.cells.each(function(c) {
20388             
20389             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20390             
20391             
20392             for (var e = 0; e < c.events.length; e++){
20393                 var ev = c.events[e];
20394                 var rows = ev.rows;
20395                 
20396                 for(var i = 0; i < rows.length; i++) {
20397                 
20398                     // how many rows should it span..
20399
20400                     var  cfg = {
20401                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20402                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20403
20404                         unselectable : "on",
20405                         cn : [
20406                             {
20407                                 cls: 'fc-event-inner',
20408                                 cn : [
20409     //                                {
20410     //                                  tag:'span',
20411     //                                  cls: 'fc-event-time',
20412     //                                  html : cells.length > 1 ? '' : ev.time
20413     //                                },
20414                                     {
20415                                       tag:'span',
20416                                       cls: 'fc-event-title',
20417                                       html : String.format('{0}', ev.title)
20418                                     }
20419
20420
20421                                 ]
20422                             },
20423                             {
20424                                 cls: 'ui-resizable-handle ui-resizable-e',
20425                                 html : '&nbsp;&nbsp;&nbsp'
20426                             }
20427
20428                         ]
20429                     };
20430
20431                     if (i == 0) {
20432                         cfg.cls += ' fc-event-start';
20433                     }
20434                     if ((i+1) == rows.length) {
20435                         cfg.cls += ' fc-event-end';
20436                     }
20437
20438                     var ctr = _this.el.select('.fc-event-container',true).first();
20439                     var cg = ctr.createChild(cfg);
20440
20441                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20442                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20443
20444                     var r = (c.more.length) ? 1 : 0;
20445                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20446                     cg.setWidth(ebox.right - sbox.x -2);
20447
20448                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20449                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20450                     cg.on('click', _this.onEventClick, _this, ev);
20451
20452                     ev.els.push(cg);
20453                     
20454                 }
20455                 
20456             }
20457             
20458             
20459             if(c.more.length){
20460                 var  cfg = {
20461                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20462                     style : 'position: absolute',
20463                     unselectable : "on",
20464                     cn : [
20465                         {
20466                             cls: 'fc-event-inner',
20467                             cn : [
20468                                 {
20469                                   tag:'span',
20470                                   cls: 'fc-event-title',
20471                                   html : 'More'
20472                                 }
20473
20474
20475                             ]
20476                         },
20477                         {
20478                             cls: 'ui-resizable-handle ui-resizable-e',
20479                             html : '&nbsp;&nbsp;&nbsp'
20480                         }
20481
20482                     ]
20483                 };
20484
20485                 var ctr = _this.el.select('.fc-event-container',true).first();
20486                 var cg = ctr.createChild(cfg);
20487
20488                 var sbox = c.select('.fc-day-content',true).first().getBox();
20489                 var ebox = c.select('.fc-day-content',true).first().getBox();
20490                 //Roo.log(cg);
20491                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20492                 cg.setWidth(ebox.right - sbox.x -2);
20493
20494                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20495                 
20496             }
20497             
20498         });
20499         
20500         
20501         
20502     },
20503     
20504     onEventEnter: function (e, el,event,d) {
20505         this.fireEvent('evententer', this, el, event);
20506     },
20507     
20508     onEventLeave: function (e, el,event,d) {
20509         this.fireEvent('eventleave', this, el, event);
20510     },
20511     
20512     onEventClick: function (e, el,event,d) {
20513         this.fireEvent('eventclick', this, el, event);
20514     },
20515     
20516     onMonthChange: function () {
20517         this.store.load();
20518     },
20519     
20520     onMoreEventClick: function(e, el, more)
20521     {
20522         var _this = this;
20523         
20524         this.calpopover.placement = 'right';
20525         this.calpopover.setTitle('More');
20526         
20527         this.calpopover.setContent('');
20528         
20529         var ctr = this.calpopover.el.select('.popover-content', true).first();
20530         
20531         Roo.each(more, function(m){
20532             var cfg = {
20533                 cls : 'fc-event-hori fc-event-draggable',
20534                 html : m.title
20535             };
20536             var cg = ctr.createChild(cfg);
20537             
20538             cg.on('click', _this.onEventClick, _this, m);
20539         });
20540         
20541         this.calpopover.show(el);
20542         
20543         
20544     },
20545     
20546     onLoad: function () 
20547     {   
20548         this.calevents = [];
20549         var cal = this;
20550         
20551         if(this.store.getCount() > 0){
20552             this.store.data.each(function(d){
20553                cal.addItem({
20554                     id : d.data.id,
20555                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20556                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20557                     time : d.data.start_time,
20558                     title : d.data.title,
20559                     description : d.data.description,
20560                     venue : d.data.venue
20561                 });
20562             });
20563         }
20564         
20565         this.renderEvents();
20566         
20567         if(this.calevents.length && this.loadMask){
20568             this.maskEl.hide();
20569         }
20570     },
20571     
20572     onBeforeLoad: function()
20573     {
20574         this.clearEvents();
20575         if(this.loadMask){
20576             this.maskEl.show();
20577         }
20578     }
20579 });
20580
20581  
20582  /*
20583  * - LGPL
20584  *
20585  * element
20586  * 
20587  */
20588
20589 /**
20590  * @class Roo.bootstrap.Popover
20591  * @extends Roo.bootstrap.Component
20592  * Bootstrap Popover class
20593  * @cfg {String} html contents of the popover   (or false to use children..)
20594  * @cfg {String} title of popover (or false to hide)
20595  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20596  * @cfg {String} trigger click || hover (or false to trigger manually)
20597  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20598  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20599  *      - if false and it has a 'parent' then it will be automatically added to that element
20600  *      - if string - Roo.get  will be called 
20601  * @cfg {Number} delay - delay before showing
20602  
20603  * @constructor
20604  * Create a new Popover
20605  * @param {Object} config The config object
20606  */
20607
20608 Roo.bootstrap.Popover = function(config){
20609     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20610     
20611     this.addEvents({
20612         // raw events
20613          /**
20614          * @event show
20615          * After the popover show
20616          * 
20617          * @param {Roo.bootstrap.Popover} this
20618          */
20619         "show" : true,
20620         /**
20621          * @event hide
20622          * After the popover hide
20623          * 
20624          * @param {Roo.bootstrap.Popover} this
20625          */
20626         "hide" : true
20627     });
20628 };
20629
20630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20631     
20632     title: false,
20633     html: false,
20634     
20635     placement : 'right',
20636     trigger : 'hover', // hover
20637     modal : false,
20638     delay : 0,
20639     
20640     over: false,
20641     
20642     can_build_overlaid : false,
20643     
20644     maskEl : false, // the mask element
20645     headerEl : false,
20646     contentEl : false,
20647     alignEl : false, // when show is called with an element - this get's stored.
20648     
20649     getChildContainer : function()
20650     {
20651         return this.contentEl;
20652         
20653     },
20654     getPopoverHeader : function()
20655     {
20656         this.title = true; // flag not to hide it..
20657         this.headerEl.addClass('p-0');
20658         return this.headerEl
20659     },
20660     
20661     
20662     getAutoCreate : function(){
20663          
20664         var cfg = {
20665            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20666            style: 'display:block',
20667            cn : [
20668                 {
20669                     cls : 'arrow'
20670                 },
20671                 {
20672                     cls : 'popover-inner ',
20673                     cn : [
20674                         {
20675                             tag: 'h3',
20676                             cls: 'popover-title popover-header',
20677                             html : this.title === false ? '' : this.title
20678                         },
20679                         {
20680                             cls : 'popover-content popover-body '  + (this.cls || ''),
20681                             html : this.html || ''
20682                         }
20683                     ]
20684                     
20685                 }
20686            ]
20687         };
20688         
20689         return cfg;
20690     },
20691     /**
20692      * @param {string} the title
20693      */
20694     setTitle: function(str)
20695     {
20696         this.title = str;
20697         if (this.el) {
20698             this.headerEl.dom.innerHTML = str;
20699         }
20700         
20701     },
20702     /**
20703      * @param {string} the body content
20704      */
20705     setContent: function(str)
20706     {
20707         this.html = str;
20708         if (this.contentEl) {
20709             this.contentEl.dom.innerHTML = str;
20710         }
20711         
20712     },
20713     // as it get's added to the bottom of the page.
20714     onRender : function(ct, position)
20715     {
20716         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20717         
20718         
20719         
20720         if(!this.el){
20721             var cfg = Roo.apply({},  this.getAutoCreate());
20722             cfg.id = Roo.id();
20723             
20724             if (this.cls) {
20725                 cfg.cls += ' ' + this.cls;
20726             }
20727             if (this.style) {
20728                 cfg.style = this.style;
20729             }
20730             //Roo.log("adding to ");
20731             this.el = Roo.get(document.body).createChild(cfg, position);
20732 //            Roo.log(this.el);
20733         }
20734         
20735         this.contentEl = this.el.select('.popover-content',true).first();
20736         this.headerEl =  this.el.select('.popover-title',true).first();
20737         
20738         var nitems = [];
20739         if(typeof(this.items) != 'undefined'){
20740             var items = this.items;
20741             delete this.items;
20742
20743             for(var i =0;i < items.length;i++) {
20744                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20745             }
20746         }
20747
20748         this.items = nitems;
20749         
20750         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20751         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20752         
20753         
20754         
20755         this.initEvents();
20756     },
20757     
20758     resizeMask : function()
20759     {
20760         this.maskEl.setSize(
20761             Roo.lib.Dom.getViewWidth(true),
20762             Roo.lib.Dom.getViewHeight(true)
20763         );
20764     },
20765     
20766     initEvents : function()
20767     {
20768         
20769         if (!this.modal) { 
20770             Roo.bootstrap.Popover.register(this);
20771         }
20772          
20773         this.arrowEl = this.el.select('.arrow',true).first();
20774         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20775         this.el.enableDisplayMode('block');
20776         this.el.hide();
20777  
20778         
20779         if (this.over === false && !this.parent()) {
20780             return; 
20781         }
20782         if (this.triggers === false) {
20783             return;
20784         }
20785          
20786         // support parent
20787         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20788         var triggers = this.trigger ? this.trigger.split(' ') : [];
20789         Roo.each(triggers, function(trigger) {
20790         
20791             if (trigger == 'click') {
20792                 on_el.on('click', this.toggle, this);
20793             } else if (trigger != 'manual') {
20794                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20795                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20796       
20797                 on_el.on(eventIn  ,this.enter, this);
20798                 on_el.on(eventOut, this.leave, this);
20799             }
20800         }, this);
20801     },
20802     
20803     
20804     // private
20805     timeout : null,
20806     hoverState : null,
20807     
20808     toggle : function () {
20809         this.hoverState == 'in' ? this.leave() : this.enter();
20810     },
20811     
20812     enter : function () {
20813         
20814         clearTimeout(this.timeout);
20815     
20816         this.hoverState = 'in';
20817     
20818         if (!this.delay || !this.delay.show) {
20819             this.show();
20820             return;
20821         }
20822         var _t = this;
20823         this.timeout = setTimeout(function () {
20824             if (_t.hoverState == 'in') {
20825                 _t.show();
20826             }
20827         }, this.delay.show)
20828     },
20829     
20830     leave : function() {
20831         clearTimeout(this.timeout);
20832     
20833         this.hoverState = 'out';
20834     
20835         if (!this.delay || !this.delay.hide) {
20836             this.hide();
20837             return;
20838         }
20839         var _t = this;
20840         this.timeout = setTimeout(function () {
20841             if (_t.hoverState == 'out') {
20842                 _t.hide();
20843             }
20844         }, this.delay.hide)
20845     },
20846     /**
20847      * Show the popover
20848      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20849      * @param {string} (left|right|top|bottom) position
20850      */
20851     show : function (on_el, placement)
20852     {
20853         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20854         on_el = on_el || false; // default to false
20855          
20856         if (!on_el) {
20857             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20858                 on_el = this.parent().el;
20859             } else if (this.over) {
20860                 on_el = Roo.get(this.over);
20861             }
20862             
20863         }
20864         
20865         this.alignEl = Roo.get( on_el );
20866
20867         if (!this.el) {
20868             this.render(document.body);
20869         }
20870         
20871         
20872          
20873         
20874         if (this.title === false) {
20875             this.headerEl.hide();
20876         }
20877         
20878        
20879         this.el.show();
20880         this.el.dom.style.display = 'block';
20881          
20882  
20883         if (this.alignEl) {
20884             this.updatePosition(this.placement, true);
20885              
20886         } else {
20887             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20888             var es = this.el.getSize();
20889             var x = Roo.lib.Dom.getViewWidth()/2;
20890             var y = Roo.lib.Dom.getViewHeight()/2;
20891             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20892             
20893         }
20894
20895         
20896         //var arrow = this.el.select('.arrow',true).first();
20897         //arrow.set(align[2], 
20898         
20899         this.el.addClass('in');
20900         
20901          
20902         
20903         this.hoverState = 'in';
20904         
20905         if (this.modal) {
20906             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20907             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20908             this.maskEl.dom.style.display = 'block';
20909             this.maskEl.addClass('show');
20910         }
20911         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20912  
20913         this.fireEvent('show', this);
20914         
20915     },
20916     /**
20917      * fire this manually after loading a grid in the table for example
20918      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20919      * @param {Boolean} try and move it if we cant get right position.
20920      */
20921     updatePosition : function(placement, try_move)
20922     {
20923         // allow for calling with no parameters
20924         placement = placement   ? placement :  this.placement;
20925         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20926         
20927         this.el.removeClass([
20928             'fade','top','bottom', 'left', 'right','in',
20929             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20930         ]);
20931         this.el.addClass(placement + ' bs-popover-' + placement);
20932         
20933         if (!this.alignEl ) {
20934             return false;
20935         }
20936         
20937         switch (placement) {
20938             case 'right':
20939                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20940                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20941                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20942                     //normal display... or moved up/down.
20943                     this.el.setXY(offset);
20944                     var xy = this.alignEl.getAnchorXY('tr', false);
20945                     xy[0]+=2;xy[1]+=5;
20946                     this.arrowEl.setXY(xy);
20947                     return true;
20948                 }
20949                 // continue through...
20950                 return this.updatePosition('left', false);
20951                 
20952             
20953             case 'left':
20954                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20955                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20956                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20957                     //normal display... or moved up/down.
20958                     this.el.setXY(offset);
20959                     var xy = this.alignEl.getAnchorXY('tl', false);
20960                     xy[0]-=10;xy[1]+=5; // << fix me
20961                     this.arrowEl.setXY(xy);
20962                     return true;
20963                 }
20964                 // call self...
20965                 return this.updatePosition('right', false);
20966             
20967             case 'top':
20968                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20969                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20970                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20971                     //normal display... or moved up/down.
20972                     this.el.setXY(offset);
20973                     var xy = this.alignEl.getAnchorXY('t', false);
20974                     xy[1]-=10; // << fix me
20975                     this.arrowEl.setXY(xy);
20976                     return true;
20977                 }
20978                 // fall through
20979                return this.updatePosition('bottom', false);
20980             
20981             case 'bottom':
20982                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20983                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20984                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20985                     //normal display... or moved up/down.
20986                     this.el.setXY(offset);
20987                     var xy = this.alignEl.getAnchorXY('b', false);
20988                      xy[1]+=2; // << fix me
20989                     this.arrowEl.setXY(xy);
20990                     return true;
20991                 }
20992                 // fall through
20993                 return this.updatePosition('top', false);
20994                 
20995             
20996         }
20997         
20998         
20999         return false;
21000     },
21001     
21002     hide : function()
21003     {
21004         this.el.setXY([0,0]);
21005         this.el.removeClass('in');
21006         this.el.hide();
21007         this.hoverState = null;
21008         this.maskEl.hide(); // always..
21009         this.fireEvent('hide', this);
21010     }
21011     
21012 });
21013
21014
21015 Roo.apply(Roo.bootstrap.Popover, {
21016
21017     alignment : {
21018         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21019         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21020         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21021         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21022     },
21023     
21024     zIndex : 20001,
21025
21026     clickHander : false,
21027     
21028     
21029
21030     onMouseDown : function(e)
21031     {
21032         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21033             /// what is nothing is showing..
21034             this.hideAll();
21035         }
21036          
21037     },
21038     
21039     
21040     popups : [],
21041     
21042     register : function(popup)
21043     {
21044         if (!Roo.bootstrap.Popover.clickHandler) {
21045             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21046         }
21047         // hide other popups.
21048         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21049         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21050         this.hideAll(); //<< why?
21051         //this.popups.push(popup);
21052     },
21053     hideAll : function()
21054     {
21055         this.popups.forEach(function(p) {
21056             p.hide();
21057         });
21058     },
21059     onShow : function() {
21060         Roo.bootstrap.Popover.popups.push(this);
21061     },
21062     onHide : function() {
21063         Roo.bootstrap.Popover.popups.remove(this);
21064     } 
21065
21066 });/*
21067  * - LGPL
21068  *
21069  * Card header - holder for the card header elements.
21070  * 
21071  */
21072
21073 /**
21074  * @class Roo.bootstrap.PopoverNav
21075  * @extends Roo.bootstrap.NavGroup
21076  * Bootstrap Popover header navigation class
21077  * @constructor
21078  * Create a new Popover Header Navigation 
21079  * @param {Object} config The config object
21080  */
21081
21082 Roo.bootstrap.PopoverNav = function(config){
21083     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21084 };
21085
21086 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21087     
21088     
21089     container_method : 'getPopoverHeader' 
21090     
21091      
21092     
21093     
21094    
21095 });
21096
21097  
21098
21099  /*
21100  * - LGPL
21101  *
21102  * Progress
21103  * 
21104  */
21105
21106 /**
21107  * @class Roo.bootstrap.Progress
21108  * @extends Roo.bootstrap.Component
21109  * Bootstrap Progress class
21110  * @cfg {Boolean} striped striped of the progress bar
21111  * @cfg {Boolean} active animated of the progress bar
21112  * 
21113  * 
21114  * @constructor
21115  * Create a new Progress
21116  * @param {Object} config The config object
21117  */
21118
21119 Roo.bootstrap.Progress = function(config){
21120     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21121 };
21122
21123 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21124     
21125     striped : false,
21126     active: false,
21127     
21128     getAutoCreate : function(){
21129         var cfg = {
21130             tag: 'div',
21131             cls: 'progress'
21132         };
21133         
21134         
21135         if(this.striped){
21136             cfg.cls += ' progress-striped';
21137         }
21138       
21139         if(this.active){
21140             cfg.cls += ' active';
21141         }
21142         
21143         
21144         return cfg;
21145     }
21146    
21147 });
21148
21149  
21150
21151  /*
21152  * - LGPL
21153  *
21154  * ProgressBar
21155  * 
21156  */
21157
21158 /**
21159  * @class Roo.bootstrap.ProgressBar
21160  * @extends Roo.bootstrap.Component
21161  * Bootstrap ProgressBar class
21162  * @cfg {Number} aria_valuenow aria-value now
21163  * @cfg {Number} aria_valuemin aria-value min
21164  * @cfg {Number} aria_valuemax aria-value max
21165  * @cfg {String} label label for the progress bar
21166  * @cfg {String} panel (success | info | warning | danger )
21167  * @cfg {String} role role of the progress bar
21168  * @cfg {String} sr_only text
21169  * 
21170  * 
21171  * @constructor
21172  * Create a new ProgressBar
21173  * @param {Object} config The config object
21174  */
21175
21176 Roo.bootstrap.ProgressBar = function(config){
21177     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21178 };
21179
21180 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21181     
21182     aria_valuenow : 0,
21183     aria_valuemin : 0,
21184     aria_valuemax : 100,
21185     label : false,
21186     panel : false,
21187     role : false,
21188     sr_only: false,
21189     
21190     getAutoCreate : function()
21191     {
21192         
21193         var cfg = {
21194             tag: 'div',
21195             cls: 'progress-bar',
21196             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21197         };
21198         
21199         if(this.sr_only){
21200             cfg.cn = {
21201                 tag: 'span',
21202                 cls: 'sr-only',
21203                 html: this.sr_only
21204             }
21205         }
21206         
21207         if(this.role){
21208             cfg.role = this.role;
21209         }
21210         
21211         if(this.aria_valuenow){
21212             cfg['aria-valuenow'] = this.aria_valuenow;
21213         }
21214         
21215         if(this.aria_valuemin){
21216             cfg['aria-valuemin'] = this.aria_valuemin;
21217         }
21218         
21219         if(this.aria_valuemax){
21220             cfg['aria-valuemax'] = this.aria_valuemax;
21221         }
21222         
21223         if(this.label && !this.sr_only){
21224             cfg.html = this.label;
21225         }
21226         
21227         if(this.panel){
21228             cfg.cls += ' progress-bar-' + this.panel;
21229         }
21230         
21231         return cfg;
21232     },
21233     
21234     update : function(aria_valuenow)
21235     {
21236         this.aria_valuenow = aria_valuenow;
21237         
21238         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21239     }
21240    
21241 });
21242
21243  
21244
21245  /*
21246  * - LGPL
21247  *
21248  * column
21249  * 
21250  */
21251
21252 /**
21253  * @class Roo.bootstrap.TabGroup
21254  * @extends Roo.bootstrap.Column
21255  * Bootstrap Column class
21256  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21257  * @cfg {Boolean} carousel true to make the group behave like a carousel
21258  * @cfg {Boolean} bullets show bullets for the panels
21259  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21260  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21261  * @cfg {Boolean} showarrow (true|false) show arrow default true
21262  * 
21263  * @constructor
21264  * Create a new TabGroup
21265  * @param {Object} config The config object
21266  */
21267
21268 Roo.bootstrap.TabGroup = function(config){
21269     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21270     if (!this.navId) {
21271         this.navId = Roo.id();
21272     }
21273     this.tabs = [];
21274     Roo.bootstrap.TabGroup.register(this);
21275     
21276 };
21277
21278 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21279     
21280     carousel : false,
21281     transition : false,
21282     bullets : 0,
21283     timer : 0,
21284     autoslide : false,
21285     slideFn : false,
21286     slideOnTouch : false,
21287     showarrow : true,
21288     
21289     getAutoCreate : function()
21290     {
21291         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21292         
21293         cfg.cls += ' tab-content';
21294         
21295         if (this.carousel) {
21296             cfg.cls += ' carousel slide';
21297             
21298             cfg.cn = [{
21299                cls : 'carousel-inner',
21300                cn : []
21301             }];
21302         
21303             if(this.bullets  && !Roo.isTouch){
21304                 
21305                 var bullets = {
21306                     cls : 'carousel-bullets',
21307                     cn : []
21308                 };
21309                
21310                 if(this.bullets_cls){
21311                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21312                 }
21313                 
21314                 bullets.cn.push({
21315                     cls : 'clear'
21316                 });
21317                 
21318                 cfg.cn[0].cn.push(bullets);
21319             }
21320             
21321             if(this.showarrow){
21322                 cfg.cn[0].cn.push({
21323                     tag : 'div',
21324                     class : 'carousel-arrow',
21325                     cn : [
21326                         {
21327                             tag : 'div',
21328                             class : 'carousel-prev',
21329                             cn : [
21330                                 {
21331                                     tag : 'i',
21332                                     class : 'fa fa-chevron-left'
21333                                 }
21334                             ]
21335                         },
21336                         {
21337                             tag : 'div',
21338                             class : 'carousel-next',
21339                             cn : [
21340                                 {
21341                                     tag : 'i',
21342                                     class : 'fa fa-chevron-right'
21343                                 }
21344                             ]
21345                         }
21346                     ]
21347                 });
21348             }
21349             
21350         }
21351         
21352         return cfg;
21353     },
21354     
21355     initEvents:  function()
21356     {
21357 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21358 //            this.el.on("touchstart", this.onTouchStart, this);
21359 //        }
21360         
21361         if(this.autoslide){
21362             var _this = this;
21363             
21364             this.slideFn = window.setInterval(function() {
21365                 _this.showPanelNext();
21366             }, this.timer);
21367         }
21368         
21369         if(this.showarrow){
21370             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21371             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21372         }
21373         
21374         
21375     },
21376     
21377 //    onTouchStart : function(e, el, o)
21378 //    {
21379 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21380 //            return;
21381 //        }
21382 //        
21383 //        this.showPanelNext();
21384 //    },
21385     
21386     
21387     getChildContainer : function()
21388     {
21389         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21390     },
21391     
21392     /**
21393     * register a Navigation item
21394     * @param {Roo.bootstrap.NavItem} the navitem to add
21395     */
21396     register : function(item)
21397     {
21398         this.tabs.push( item);
21399         item.navId = this.navId; // not really needed..
21400         this.addBullet();
21401     
21402     },
21403     
21404     getActivePanel : function()
21405     {
21406         var r = false;
21407         Roo.each(this.tabs, function(t) {
21408             if (t.active) {
21409                 r = t;
21410                 return false;
21411             }
21412             return null;
21413         });
21414         return r;
21415         
21416     },
21417     getPanelByName : function(n)
21418     {
21419         var r = false;
21420         Roo.each(this.tabs, function(t) {
21421             if (t.tabId == n) {
21422                 r = t;
21423                 return false;
21424             }
21425             return null;
21426         });
21427         return r;
21428     },
21429     indexOfPanel : function(p)
21430     {
21431         var r = false;
21432         Roo.each(this.tabs, function(t,i) {
21433             if (t.tabId == p.tabId) {
21434                 r = i;
21435                 return false;
21436             }
21437             return null;
21438         });
21439         return r;
21440     },
21441     /**
21442      * show a specific panel
21443      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21444      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21445      */
21446     showPanel : function (pan)
21447     {
21448         if(this.transition || typeof(pan) == 'undefined'){
21449             Roo.log("waiting for the transitionend");
21450             return false;
21451         }
21452         
21453         if (typeof(pan) == 'number') {
21454             pan = this.tabs[pan];
21455         }
21456         
21457         if (typeof(pan) == 'string') {
21458             pan = this.getPanelByName(pan);
21459         }
21460         
21461         var cur = this.getActivePanel();
21462         
21463         if(!pan || !cur){
21464             Roo.log('pan or acitve pan is undefined');
21465             return false;
21466         }
21467         
21468         if (pan.tabId == this.getActivePanel().tabId) {
21469             return true;
21470         }
21471         
21472         if (false === cur.fireEvent('beforedeactivate')) {
21473             return false;
21474         }
21475         
21476         if(this.bullets > 0 && !Roo.isTouch){
21477             this.setActiveBullet(this.indexOfPanel(pan));
21478         }
21479         
21480         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21481             
21482             //class="carousel-item carousel-item-next carousel-item-left"
21483             
21484             this.transition = true;
21485             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21486             var lr = dir == 'next' ? 'left' : 'right';
21487             pan.el.addClass(dir); // or prev
21488             pan.el.addClass('carousel-item-' + dir); // or prev
21489             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21490             cur.el.addClass(lr); // or right
21491             pan.el.addClass(lr);
21492             cur.el.addClass('carousel-item-' +lr); // or right
21493             pan.el.addClass('carousel-item-' +lr);
21494             
21495             
21496             var _this = this;
21497             cur.el.on('transitionend', function() {
21498                 Roo.log("trans end?");
21499                 
21500                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21501                 pan.setActive(true);
21502                 
21503                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21504                 cur.setActive(false);
21505                 
21506                 _this.transition = false;
21507                 
21508             }, this, { single:  true } );
21509             
21510             return true;
21511         }
21512         
21513         cur.setActive(false);
21514         pan.setActive(true);
21515         
21516         return true;
21517         
21518     },
21519     showPanelNext : function()
21520     {
21521         var i = this.indexOfPanel(this.getActivePanel());
21522         
21523         if (i >= this.tabs.length - 1 && !this.autoslide) {
21524             return;
21525         }
21526         
21527         if (i >= this.tabs.length - 1 && this.autoslide) {
21528             i = -1;
21529         }
21530         
21531         this.showPanel(this.tabs[i+1]);
21532     },
21533     
21534     showPanelPrev : function()
21535     {
21536         var i = this.indexOfPanel(this.getActivePanel());
21537         
21538         if (i  < 1 && !this.autoslide) {
21539             return;
21540         }
21541         
21542         if (i < 1 && this.autoslide) {
21543             i = this.tabs.length;
21544         }
21545         
21546         this.showPanel(this.tabs[i-1]);
21547     },
21548     
21549     
21550     addBullet: function()
21551     {
21552         if(!this.bullets || Roo.isTouch){
21553             return;
21554         }
21555         var ctr = this.el.select('.carousel-bullets',true).first();
21556         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21557         var bullet = ctr.createChild({
21558             cls : 'bullet bullet-' + i
21559         },ctr.dom.lastChild);
21560         
21561         
21562         var _this = this;
21563         
21564         bullet.on('click', (function(e, el, o, ii, t){
21565
21566             e.preventDefault();
21567
21568             this.showPanel(ii);
21569
21570             if(this.autoslide && this.slideFn){
21571                 clearInterval(this.slideFn);
21572                 this.slideFn = window.setInterval(function() {
21573                     _this.showPanelNext();
21574                 }, this.timer);
21575             }
21576
21577         }).createDelegate(this, [i, bullet], true));
21578                 
21579         
21580     },
21581      
21582     setActiveBullet : function(i)
21583     {
21584         if(Roo.isTouch){
21585             return;
21586         }
21587         
21588         Roo.each(this.el.select('.bullet', true).elements, function(el){
21589             el.removeClass('selected');
21590         });
21591
21592         var bullet = this.el.select('.bullet-' + i, true).first();
21593         
21594         if(!bullet){
21595             return;
21596         }
21597         
21598         bullet.addClass('selected');
21599     }
21600     
21601     
21602   
21603 });
21604
21605  
21606
21607  
21608  
21609 Roo.apply(Roo.bootstrap.TabGroup, {
21610     
21611     groups: {},
21612      /**
21613     * register a Navigation Group
21614     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21615     */
21616     register : function(navgrp)
21617     {
21618         this.groups[navgrp.navId] = navgrp;
21619         
21620     },
21621     /**
21622     * fetch a Navigation Group based on the navigation ID
21623     * if one does not exist , it will get created.
21624     * @param {string} the navgroup to add
21625     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21626     */
21627     get: function(navId) {
21628         if (typeof(this.groups[navId]) == 'undefined') {
21629             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21630         }
21631         return this.groups[navId] ;
21632     }
21633     
21634     
21635     
21636 });
21637
21638  /*
21639  * - LGPL
21640  *
21641  * TabPanel
21642  * 
21643  */
21644
21645 /**
21646  * @class Roo.bootstrap.TabPanel
21647  * @extends Roo.bootstrap.Component
21648  * Bootstrap TabPanel class
21649  * @cfg {Boolean} active panel active
21650  * @cfg {String} html panel content
21651  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21652  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21653  * @cfg {String} href click to link..
21654  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21655  * 
21656  * 
21657  * @constructor
21658  * Create a new TabPanel
21659  * @param {Object} config The config object
21660  */
21661
21662 Roo.bootstrap.TabPanel = function(config){
21663     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21664     this.addEvents({
21665         /**
21666              * @event changed
21667              * Fires when the active status changes
21668              * @param {Roo.bootstrap.TabPanel} this
21669              * @param {Boolean} state the new state
21670             
21671          */
21672         'changed': true,
21673         /**
21674              * @event beforedeactivate
21675              * Fires before a tab is de-activated - can be used to do validation on a form.
21676              * @param {Roo.bootstrap.TabPanel} this
21677              * @return {Boolean} false if there is an error
21678             
21679          */
21680         'beforedeactivate': true
21681      });
21682     
21683     this.tabId = this.tabId || Roo.id();
21684   
21685 };
21686
21687 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21688     
21689     active: false,
21690     html: false,
21691     tabId: false,
21692     navId : false,
21693     href : '',
21694     touchSlide : false,
21695     getAutoCreate : function(){
21696         
21697         
21698         var cfg = {
21699             tag: 'div',
21700             // item is needed for carousel - not sure if it has any effect otherwise
21701             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21702             html: this.html || ''
21703         };
21704         
21705         if(this.active){
21706             cfg.cls += ' active';
21707         }
21708         
21709         if(this.tabId){
21710             cfg.tabId = this.tabId;
21711         }
21712         
21713         
21714         
21715         return cfg;
21716     },
21717     
21718     initEvents:  function()
21719     {
21720         var p = this.parent();
21721         
21722         this.navId = this.navId || p.navId;
21723         
21724         if (typeof(this.navId) != 'undefined') {
21725             // not really needed.. but just in case.. parent should be a NavGroup.
21726             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21727             
21728             tg.register(this);
21729             
21730             var i = tg.tabs.length - 1;
21731             
21732             if(this.active && tg.bullets > 0 && i < tg.bullets){
21733                 tg.setActiveBullet(i);
21734             }
21735         }
21736         
21737         this.el.on('click', this.onClick, this);
21738         
21739         if(Roo.isTouch && this.touchSlide){
21740             this.el.on("touchstart", this.onTouchStart, this);
21741             this.el.on("touchmove", this.onTouchMove, this);
21742             this.el.on("touchend", this.onTouchEnd, this);
21743         }
21744         
21745     },
21746     
21747     onRender : function(ct, position)
21748     {
21749         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21750     },
21751     
21752     setActive : function(state)
21753     {
21754         Roo.log("panel - set active " + this.tabId + "=" + state);
21755         
21756         this.active = state;
21757         if (!state) {
21758             this.el.removeClass('active');
21759             
21760         } else  if (!this.el.hasClass('active')) {
21761             this.el.addClass('active');
21762         }
21763         
21764         this.fireEvent('changed', this, state);
21765     },
21766     
21767     onClick : function(e)
21768     {
21769         e.preventDefault();
21770         
21771         if(!this.href.length){
21772             return;
21773         }
21774         
21775         window.location.href = this.href;
21776     },
21777     
21778     startX : 0,
21779     startY : 0,
21780     endX : 0,
21781     endY : 0,
21782     swiping : false,
21783     
21784     onTouchStart : function(e)
21785     {
21786         this.swiping = false;
21787         
21788         this.startX = e.browserEvent.touches[0].clientX;
21789         this.startY = e.browserEvent.touches[0].clientY;
21790     },
21791     
21792     onTouchMove : function(e)
21793     {
21794         this.swiping = true;
21795         
21796         this.endX = e.browserEvent.touches[0].clientX;
21797         this.endY = e.browserEvent.touches[0].clientY;
21798     },
21799     
21800     onTouchEnd : function(e)
21801     {
21802         if(!this.swiping){
21803             this.onClick(e);
21804             return;
21805         }
21806         
21807         var tabGroup = this.parent();
21808         
21809         if(this.endX > this.startX){ // swiping right
21810             tabGroup.showPanelPrev();
21811             return;
21812         }
21813         
21814         if(this.startX > this.endX){ // swiping left
21815             tabGroup.showPanelNext();
21816             return;
21817         }
21818     }
21819     
21820     
21821 });
21822  
21823
21824  
21825
21826  /*
21827  * - LGPL
21828  *
21829  * DateField
21830  * 
21831  */
21832
21833 /**
21834  * @class Roo.bootstrap.DateField
21835  * @extends Roo.bootstrap.Input
21836  * Bootstrap DateField class
21837  * @cfg {Number} weekStart default 0
21838  * @cfg {String} viewMode default empty, (months|years)
21839  * @cfg {String} minViewMode default empty, (months|years)
21840  * @cfg {Number} startDate default -Infinity
21841  * @cfg {Number} endDate default Infinity
21842  * @cfg {Boolean} todayHighlight default false
21843  * @cfg {Boolean} todayBtn default false
21844  * @cfg {Boolean} calendarWeeks default false
21845  * @cfg {Object} daysOfWeekDisabled default empty
21846  * @cfg {Boolean} singleMode default false (true | false)
21847  * 
21848  * @cfg {Boolean} keyboardNavigation default true
21849  * @cfg {String} language default en
21850  * 
21851  * @constructor
21852  * Create a new DateField
21853  * @param {Object} config The config object
21854  */
21855
21856 Roo.bootstrap.DateField = function(config){
21857     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21858      this.addEvents({
21859             /**
21860              * @event show
21861              * Fires when this field show.
21862              * @param {Roo.bootstrap.DateField} this
21863              * @param {Mixed} date The date value
21864              */
21865             show : true,
21866             /**
21867              * @event show
21868              * Fires when this field hide.
21869              * @param {Roo.bootstrap.DateField} this
21870              * @param {Mixed} date The date value
21871              */
21872             hide : true,
21873             /**
21874              * @event select
21875              * Fires when select a date.
21876              * @param {Roo.bootstrap.DateField} this
21877              * @param {Mixed} date The date value
21878              */
21879             select : true,
21880             /**
21881              * @event beforeselect
21882              * Fires when before select a date.
21883              * @param {Roo.bootstrap.DateField} this
21884              * @param {Mixed} date The date value
21885              */
21886             beforeselect : true
21887         });
21888 };
21889
21890 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21891     
21892     /**
21893      * @cfg {String} format
21894      * The default date format string which can be overriden for localization support.  The format must be
21895      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21896      */
21897     format : "m/d/y",
21898     /**
21899      * @cfg {String} altFormats
21900      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21901      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21902      */
21903     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21904     
21905     weekStart : 0,
21906     
21907     viewMode : '',
21908     
21909     minViewMode : '',
21910     
21911     todayHighlight : false,
21912     
21913     todayBtn: false,
21914     
21915     language: 'en',
21916     
21917     keyboardNavigation: true,
21918     
21919     calendarWeeks: false,
21920     
21921     startDate: -Infinity,
21922     
21923     endDate: Infinity,
21924     
21925     daysOfWeekDisabled: [],
21926     
21927     _events: [],
21928     
21929     singleMode : false,
21930     
21931     UTCDate: function()
21932     {
21933         return new Date(Date.UTC.apply(Date, arguments));
21934     },
21935     
21936     UTCToday: function()
21937     {
21938         var today = new Date();
21939         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21940     },
21941     
21942     getDate: function() {
21943             var d = this.getUTCDate();
21944             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21945     },
21946     
21947     getUTCDate: function() {
21948             return this.date;
21949     },
21950     
21951     setDate: function(d) {
21952             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21953     },
21954     
21955     setUTCDate: function(d) {
21956             this.date = d;
21957             this.setValue(this.formatDate(this.date));
21958     },
21959         
21960     onRender: function(ct, position)
21961     {
21962         
21963         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21964         
21965         this.language = this.language || 'en';
21966         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21967         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21968         
21969         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21970         this.format = this.format || 'm/d/y';
21971         this.isInline = false;
21972         this.isInput = true;
21973         this.component = this.el.select('.add-on', true).first() || false;
21974         this.component = (this.component && this.component.length === 0) ? false : this.component;
21975         this.hasInput = this.component && this.inputEl().length;
21976         
21977         if (typeof(this.minViewMode === 'string')) {
21978             switch (this.minViewMode) {
21979                 case 'months':
21980                     this.minViewMode = 1;
21981                     break;
21982                 case 'years':
21983                     this.minViewMode = 2;
21984                     break;
21985                 default:
21986                     this.minViewMode = 0;
21987                     break;
21988             }
21989         }
21990         
21991         if (typeof(this.viewMode === 'string')) {
21992             switch (this.viewMode) {
21993                 case 'months':
21994                     this.viewMode = 1;
21995                     break;
21996                 case 'years':
21997                     this.viewMode = 2;
21998                     break;
21999                 default:
22000                     this.viewMode = 0;
22001                     break;
22002             }
22003         }
22004                 
22005         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22006         
22007 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22008         
22009         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22010         
22011         this.picker().on('mousedown', this.onMousedown, this);
22012         this.picker().on('click', this.onClick, this);
22013         
22014         this.picker().addClass('datepicker-dropdown');
22015         
22016         this.startViewMode = this.viewMode;
22017         
22018         if(this.singleMode){
22019             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22020                 v.setVisibilityMode(Roo.Element.DISPLAY);
22021                 v.hide();
22022             });
22023             
22024             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22025                 v.setStyle('width', '189px');
22026             });
22027         }
22028         
22029         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22030             if(!this.calendarWeeks){
22031                 v.remove();
22032                 return;
22033             }
22034             
22035             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22036             v.attr('colspan', function(i, val){
22037                 return parseInt(val) + 1;
22038             });
22039         });
22040                         
22041         
22042         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22043         
22044         this.setStartDate(this.startDate);
22045         this.setEndDate(this.endDate);
22046         
22047         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22048         
22049         this.fillDow();
22050         this.fillMonths();
22051         this.update();
22052         this.showMode();
22053         
22054         if(this.isInline) {
22055             this.showPopup();
22056         }
22057     },
22058     
22059     picker : function()
22060     {
22061         return this.pickerEl;
22062 //        return this.el.select('.datepicker', true).first();
22063     },
22064     
22065     fillDow: function()
22066     {
22067         var dowCnt = this.weekStart;
22068         
22069         var dow = {
22070             tag: 'tr',
22071             cn: [
22072                 
22073             ]
22074         };
22075         
22076         if(this.calendarWeeks){
22077             dow.cn.push({
22078                 tag: 'th',
22079                 cls: 'cw',
22080                 html: '&nbsp;'
22081             })
22082         }
22083         
22084         while (dowCnt < this.weekStart + 7) {
22085             dow.cn.push({
22086                 tag: 'th',
22087                 cls: 'dow',
22088                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22089             });
22090         }
22091         
22092         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22093     },
22094     
22095     fillMonths: function()
22096     {    
22097         var i = 0;
22098         var months = this.picker().select('>.datepicker-months td', true).first();
22099         
22100         months.dom.innerHTML = '';
22101         
22102         while (i < 12) {
22103             var month = {
22104                 tag: 'span',
22105                 cls: 'month',
22106                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22107             };
22108             
22109             months.createChild(month);
22110         }
22111         
22112     },
22113     
22114     update: function()
22115     {
22116         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;
22117         
22118         if (this.date < this.startDate) {
22119             this.viewDate = new Date(this.startDate);
22120         } else if (this.date > this.endDate) {
22121             this.viewDate = new Date(this.endDate);
22122         } else {
22123             this.viewDate = new Date(this.date);
22124         }
22125         
22126         this.fill();
22127     },
22128     
22129     fill: function() 
22130     {
22131         var d = new Date(this.viewDate),
22132                 year = d.getUTCFullYear(),
22133                 month = d.getUTCMonth(),
22134                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22135                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22136                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22137                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22138                 currentDate = this.date && this.date.valueOf(),
22139                 today = this.UTCToday();
22140         
22141         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22142         
22143 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22144         
22145 //        this.picker.select('>tfoot th.today').
22146 //                                              .text(dates[this.language].today)
22147 //                                              .toggle(this.todayBtn !== false);
22148     
22149         this.updateNavArrows();
22150         this.fillMonths();
22151                                                 
22152         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22153         
22154         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22155          
22156         prevMonth.setUTCDate(day);
22157         
22158         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22159         
22160         var nextMonth = new Date(prevMonth);
22161         
22162         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22163         
22164         nextMonth = nextMonth.valueOf();
22165         
22166         var fillMonths = false;
22167         
22168         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22169         
22170         while(prevMonth.valueOf() <= nextMonth) {
22171             var clsName = '';
22172             
22173             if (prevMonth.getUTCDay() === this.weekStart) {
22174                 if(fillMonths){
22175                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22176                 }
22177                     
22178                 fillMonths = {
22179                     tag: 'tr',
22180                     cn: []
22181                 };
22182                 
22183                 if(this.calendarWeeks){
22184                     // ISO 8601: First week contains first thursday.
22185                     // ISO also states week starts on Monday, but we can be more abstract here.
22186                     var
22187                     // Start of current week: based on weekstart/current date
22188                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22189                     // Thursday of this week
22190                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22191                     // First Thursday of year, year from thursday
22192                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22193                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22194                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22195                     
22196                     fillMonths.cn.push({
22197                         tag: 'td',
22198                         cls: 'cw',
22199                         html: calWeek
22200                     });
22201                 }
22202             }
22203             
22204             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22205                 clsName += ' old';
22206             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22207                 clsName += ' new';
22208             }
22209             if (this.todayHighlight &&
22210                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22211                 prevMonth.getUTCMonth() == today.getMonth() &&
22212                 prevMonth.getUTCDate() == today.getDate()) {
22213                 clsName += ' today';
22214             }
22215             
22216             if (currentDate && prevMonth.valueOf() === currentDate) {
22217                 clsName += ' active';
22218             }
22219             
22220             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22221                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22222                     clsName += ' disabled';
22223             }
22224             
22225             fillMonths.cn.push({
22226                 tag: 'td',
22227                 cls: 'day ' + clsName,
22228                 html: prevMonth.getDate()
22229             });
22230             
22231             prevMonth.setDate(prevMonth.getDate()+1);
22232         }
22233           
22234         var currentYear = this.date && this.date.getUTCFullYear();
22235         var currentMonth = this.date && this.date.getUTCMonth();
22236         
22237         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22238         
22239         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22240             v.removeClass('active');
22241             
22242             if(currentYear === year && k === currentMonth){
22243                 v.addClass('active');
22244             }
22245             
22246             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22247                 v.addClass('disabled');
22248             }
22249             
22250         });
22251         
22252         
22253         year = parseInt(year/10, 10) * 10;
22254         
22255         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22256         
22257         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22258         
22259         year -= 1;
22260         for (var i = -1; i < 11; i++) {
22261             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22262                 tag: 'span',
22263                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22264                 html: year
22265             });
22266             
22267             year += 1;
22268         }
22269     },
22270     
22271     showMode: function(dir) 
22272     {
22273         if (dir) {
22274             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22275         }
22276         
22277         Roo.each(this.picker().select('>div',true).elements, function(v){
22278             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22279             v.hide();
22280         });
22281         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22282     },
22283     
22284     place: function()
22285     {
22286         if(this.isInline) {
22287             return;
22288         }
22289         
22290         this.picker().removeClass(['bottom', 'top']);
22291         
22292         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22293             /*
22294              * place to the top of element!
22295              *
22296              */
22297             
22298             this.picker().addClass('top');
22299             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22300             
22301             return;
22302         }
22303         
22304         this.picker().addClass('bottom');
22305         
22306         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22307     },
22308     
22309     parseDate : function(value)
22310     {
22311         if(!value || value instanceof Date){
22312             return value;
22313         }
22314         var v = Date.parseDate(value, this.format);
22315         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22316             v = Date.parseDate(value, 'Y-m-d');
22317         }
22318         if(!v && this.altFormats){
22319             if(!this.altFormatsArray){
22320                 this.altFormatsArray = this.altFormats.split("|");
22321             }
22322             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22323                 v = Date.parseDate(value, this.altFormatsArray[i]);
22324             }
22325         }
22326         return v;
22327     },
22328     
22329     formatDate : function(date, fmt)
22330     {   
22331         return (!date || !(date instanceof Date)) ?
22332         date : date.dateFormat(fmt || this.format);
22333     },
22334     
22335     onFocus : function()
22336     {
22337         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22338         this.showPopup();
22339     },
22340     
22341     onBlur : function()
22342     {
22343         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22344         
22345         var d = this.inputEl().getValue();
22346         
22347         this.setValue(d);
22348                 
22349         this.hidePopup();
22350     },
22351     
22352     showPopup : function()
22353     {
22354         this.picker().show();
22355         this.update();
22356         this.place();
22357         
22358         this.fireEvent('showpopup', this, this.date);
22359     },
22360     
22361     hidePopup : function()
22362     {
22363         if(this.isInline) {
22364             return;
22365         }
22366         this.picker().hide();
22367         this.viewMode = this.startViewMode;
22368         this.showMode();
22369         
22370         this.fireEvent('hidepopup', this, this.date);
22371         
22372     },
22373     
22374     onMousedown: function(e)
22375     {
22376         e.stopPropagation();
22377         e.preventDefault();
22378     },
22379     
22380     keyup: function(e)
22381     {
22382         Roo.bootstrap.DateField.superclass.keyup.call(this);
22383         this.update();
22384     },
22385
22386     setValue: function(v)
22387     {
22388         if(this.fireEvent('beforeselect', this, v) !== false){
22389             var d = new Date(this.parseDate(v) ).clearTime();
22390         
22391             if(isNaN(d.getTime())){
22392                 this.date = this.viewDate = '';
22393                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22394                 return;
22395             }
22396
22397             v = this.formatDate(d);
22398
22399             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22400
22401             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22402
22403             this.update();
22404
22405             this.fireEvent('select', this, this.date);
22406         }
22407     },
22408     
22409     getValue: function()
22410     {
22411         return this.formatDate(this.date);
22412     },
22413     
22414     fireKey: function(e)
22415     {
22416         if (!this.picker().isVisible()){
22417             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22418                 this.showPopup();
22419             }
22420             return;
22421         }
22422         
22423         var dateChanged = false,
22424         dir, day, month,
22425         newDate, newViewDate;
22426         
22427         switch(e.keyCode){
22428             case 27: // escape
22429                 this.hidePopup();
22430                 e.preventDefault();
22431                 break;
22432             case 37: // left
22433             case 39: // right
22434                 if (!this.keyboardNavigation) {
22435                     break;
22436                 }
22437                 dir = e.keyCode == 37 ? -1 : 1;
22438                 
22439                 if (e.ctrlKey){
22440                     newDate = this.moveYear(this.date, dir);
22441                     newViewDate = this.moveYear(this.viewDate, dir);
22442                 } else if (e.shiftKey){
22443                     newDate = this.moveMonth(this.date, dir);
22444                     newViewDate = this.moveMonth(this.viewDate, dir);
22445                 } else {
22446                     newDate = new Date(this.date);
22447                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22448                     newViewDate = new Date(this.viewDate);
22449                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22450                 }
22451                 if (this.dateWithinRange(newDate)){
22452                     this.date = newDate;
22453                     this.viewDate = newViewDate;
22454                     this.setValue(this.formatDate(this.date));
22455 //                    this.update();
22456                     e.preventDefault();
22457                     dateChanged = true;
22458                 }
22459                 break;
22460             case 38: // up
22461             case 40: // down
22462                 if (!this.keyboardNavigation) {
22463                     break;
22464                 }
22465                 dir = e.keyCode == 38 ? -1 : 1;
22466                 if (e.ctrlKey){
22467                     newDate = this.moveYear(this.date, dir);
22468                     newViewDate = this.moveYear(this.viewDate, dir);
22469                 } else if (e.shiftKey){
22470                     newDate = this.moveMonth(this.date, dir);
22471                     newViewDate = this.moveMonth(this.viewDate, dir);
22472                 } else {
22473                     newDate = new Date(this.date);
22474                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22475                     newViewDate = new Date(this.viewDate);
22476                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22477                 }
22478                 if (this.dateWithinRange(newDate)){
22479                     this.date = newDate;
22480                     this.viewDate = newViewDate;
22481                     this.setValue(this.formatDate(this.date));
22482 //                    this.update();
22483                     e.preventDefault();
22484                     dateChanged = true;
22485                 }
22486                 break;
22487             case 13: // enter
22488                 this.setValue(this.formatDate(this.date));
22489                 this.hidePopup();
22490                 e.preventDefault();
22491                 break;
22492             case 9: // tab
22493                 this.setValue(this.formatDate(this.date));
22494                 this.hidePopup();
22495                 break;
22496             case 16: // shift
22497             case 17: // ctrl
22498             case 18: // alt
22499                 break;
22500             default :
22501                 this.hidePopup();
22502                 
22503         }
22504     },
22505     
22506     
22507     onClick: function(e) 
22508     {
22509         e.stopPropagation();
22510         e.preventDefault();
22511         
22512         var target = e.getTarget();
22513         
22514         if(target.nodeName.toLowerCase() === 'i'){
22515             target = Roo.get(target).dom.parentNode;
22516         }
22517         
22518         var nodeName = target.nodeName;
22519         var className = target.className;
22520         var html = target.innerHTML;
22521         //Roo.log(nodeName);
22522         
22523         switch(nodeName.toLowerCase()) {
22524             case 'th':
22525                 switch(className) {
22526                     case 'switch':
22527                         this.showMode(1);
22528                         break;
22529                     case 'prev':
22530                     case 'next':
22531                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22532                         switch(this.viewMode){
22533                                 case 0:
22534                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22535                                         break;
22536                                 case 1:
22537                                 case 2:
22538                                         this.viewDate = this.moveYear(this.viewDate, dir);
22539                                         break;
22540                         }
22541                         this.fill();
22542                         break;
22543                     case 'today':
22544                         var date = new Date();
22545                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22546 //                        this.fill()
22547                         this.setValue(this.formatDate(this.date));
22548                         
22549                         this.hidePopup();
22550                         break;
22551                 }
22552                 break;
22553             case 'span':
22554                 if (className.indexOf('disabled') < 0) {
22555                 if (!this.viewDate) {
22556                     this.viewDate = new Date();
22557                 }
22558                 this.viewDate.setUTCDate(1);
22559                     if (className.indexOf('month') > -1) {
22560                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22561                     } else {
22562                         var year = parseInt(html, 10) || 0;
22563                         this.viewDate.setUTCFullYear(year);
22564                         
22565                     }
22566                     
22567                     if(this.singleMode){
22568                         this.setValue(this.formatDate(this.viewDate));
22569                         this.hidePopup();
22570                         return;
22571                     }
22572                     
22573                     this.showMode(-1);
22574                     this.fill();
22575                 }
22576                 break;
22577                 
22578             case 'td':
22579                 //Roo.log(className);
22580                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22581                     var day = parseInt(html, 10) || 1;
22582                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22583                         month = (this.viewDate || new Date()).getUTCMonth();
22584
22585                     if (className.indexOf('old') > -1) {
22586                         if(month === 0 ){
22587                             month = 11;
22588                             year -= 1;
22589                         }else{
22590                             month -= 1;
22591                         }
22592                     } else if (className.indexOf('new') > -1) {
22593                         if (month == 11) {
22594                             month = 0;
22595                             year += 1;
22596                         } else {
22597                             month += 1;
22598                         }
22599                     }
22600                     //Roo.log([year,month,day]);
22601                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22602                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22603 //                    this.fill();
22604                     //Roo.log(this.formatDate(this.date));
22605                     this.setValue(this.formatDate(this.date));
22606                     this.hidePopup();
22607                 }
22608                 break;
22609         }
22610     },
22611     
22612     setStartDate: function(startDate)
22613     {
22614         this.startDate = startDate || -Infinity;
22615         if (this.startDate !== -Infinity) {
22616             this.startDate = this.parseDate(this.startDate);
22617         }
22618         this.update();
22619         this.updateNavArrows();
22620     },
22621
22622     setEndDate: function(endDate)
22623     {
22624         this.endDate = endDate || Infinity;
22625         if (this.endDate !== Infinity) {
22626             this.endDate = this.parseDate(this.endDate);
22627         }
22628         this.update();
22629         this.updateNavArrows();
22630     },
22631     
22632     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22633     {
22634         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22635         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22636             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22637         }
22638         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22639             return parseInt(d, 10);
22640         });
22641         this.update();
22642         this.updateNavArrows();
22643     },
22644     
22645     updateNavArrows: function() 
22646     {
22647         if(this.singleMode){
22648             return;
22649         }
22650         
22651         var d = new Date(this.viewDate),
22652         year = d.getUTCFullYear(),
22653         month = d.getUTCMonth();
22654         
22655         Roo.each(this.picker().select('.prev', true).elements, function(v){
22656             v.show();
22657             switch (this.viewMode) {
22658                 case 0:
22659
22660                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22661                         v.hide();
22662                     }
22663                     break;
22664                 case 1:
22665                 case 2:
22666                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22667                         v.hide();
22668                     }
22669                     break;
22670             }
22671         });
22672         
22673         Roo.each(this.picker().select('.next', true).elements, function(v){
22674             v.show();
22675             switch (this.viewMode) {
22676                 case 0:
22677
22678                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22679                         v.hide();
22680                     }
22681                     break;
22682                 case 1:
22683                 case 2:
22684                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22685                         v.hide();
22686                     }
22687                     break;
22688             }
22689         })
22690     },
22691     
22692     moveMonth: function(date, dir)
22693     {
22694         if (!dir) {
22695             return date;
22696         }
22697         var new_date = new Date(date.valueOf()),
22698         day = new_date.getUTCDate(),
22699         month = new_date.getUTCMonth(),
22700         mag = Math.abs(dir),
22701         new_month, test;
22702         dir = dir > 0 ? 1 : -1;
22703         if (mag == 1){
22704             test = dir == -1
22705             // If going back one month, make sure month is not current month
22706             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22707             ? function(){
22708                 return new_date.getUTCMonth() == month;
22709             }
22710             // If going forward one month, make sure month is as expected
22711             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22712             : function(){
22713                 return new_date.getUTCMonth() != new_month;
22714             };
22715             new_month = month + dir;
22716             new_date.setUTCMonth(new_month);
22717             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22718             if (new_month < 0 || new_month > 11) {
22719                 new_month = (new_month + 12) % 12;
22720             }
22721         } else {
22722             // For magnitudes >1, move one month at a time...
22723             for (var i=0; i<mag; i++) {
22724                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22725                 new_date = this.moveMonth(new_date, dir);
22726             }
22727             // ...then reset the day, keeping it in the new month
22728             new_month = new_date.getUTCMonth();
22729             new_date.setUTCDate(day);
22730             test = function(){
22731                 return new_month != new_date.getUTCMonth();
22732             };
22733         }
22734         // Common date-resetting loop -- if date is beyond end of month, make it
22735         // end of month
22736         while (test()){
22737             new_date.setUTCDate(--day);
22738             new_date.setUTCMonth(new_month);
22739         }
22740         return new_date;
22741     },
22742
22743     moveYear: function(date, dir)
22744     {
22745         return this.moveMonth(date, dir*12);
22746     },
22747
22748     dateWithinRange: function(date)
22749     {
22750         return date >= this.startDate && date <= this.endDate;
22751     },
22752
22753     
22754     remove: function() 
22755     {
22756         this.picker().remove();
22757     },
22758     
22759     validateValue : function(value)
22760     {
22761         if(this.getVisibilityEl().hasClass('hidden')){
22762             return true;
22763         }
22764         
22765         if(value.length < 1)  {
22766             if(this.allowBlank){
22767                 return true;
22768             }
22769             return false;
22770         }
22771         
22772         if(value.length < this.minLength){
22773             return false;
22774         }
22775         if(value.length > this.maxLength){
22776             return false;
22777         }
22778         if(this.vtype){
22779             var vt = Roo.form.VTypes;
22780             if(!vt[this.vtype](value, this)){
22781                 return false;
22782             }
22783         }
22784         if(typeof this.validator == "function"){
22785             var msg = this.validator(value);
22786             if(msg !== true){
22787                 return false;
22788             }
22789         }
22790         
22791         if(this.regex && !this.regex.test(value)){
22792             return false;
22793         }
22794         
22795         if(typeof(this.parseDate(value)) == 'undefined'){
22796             return false;
22797         }
22798         
22799         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22800             return false;
22801         }      
22802         
22803         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22804             return false;
22805         } 
22806         
22807         
22808         return true;
22809     },
22810     
22811     reset : function()
22812     {
22813         this.date = this.viewDate = '';
22814         
22815         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22816     }
22817    
22818 });
22819
22820 Roo.apply(Roo.bootstrap.DateField,  {
22821     
22822     head : {
22823         tag: 'thead',
22824         cn: [
22825         {
22826             tag: 'tr',
22827             cn: [
22828             {
22829                 tag: 'th',
22830                 cls: 'prev',
22831                 html: '<i class="fa fa-arrow-left"/>'
22832             },
22833             {
22834                 tag: 'th',
22835                 cls: 'switch',
22836                 colspan: '5'
22837             },
22838             {
22839                 tag: 'th',
22840                 cls: 'next',
22841                 html: '<i class="fa fa-arrow-right"/>'
22842             }
22843
22844             ]
22845         }
22846         ]
22847     },
22848     
22849     content : {
22850         tag: 'tbody',
22851         cn: [
22852         {
22853             tag: 'tr',
22854             cn: [
22855             {
22856                 tag: 'td',
22857                 colspan: '7'
22858             }
22859             ]
22860         }
22861         ]
22862     },
22863     
22864     footer : {
22865         tag: 'tfoot',
22866         cn: [
22867         {
22868             tag: 'tr',
22869             cn: [
22870             {
22871                 tag: 'th',
22872                 colspan: '7',
22873                 cls: 'today'
22874             }
22875                     
22876             ]
22877         }
22878         ]
22879     },
22880     
22881     dates:{
22882         en: {
22883             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22884             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22885             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22886             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22887             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22888             today: "Today"
22889         }
22890     },
22891     
22892     modes: [
22893     {
22894         clsName: 'days',
22895         navFnc: 'Month',
22896         navStep: 1
22897     },
22898     {
22899         clsName: 'months',
22900         navFnc: 'FullYear',
22901         navStep: 1
22902     },
22903     {
22904         clsName: 'years',
22905         navFnc: 'FullYear',
22906         navStep: 10
22907     }]
22908 });
22909
22910 Roo.apply(Roo.bootstrap.DateField,  {
22911   
22912     template : {
22913         tag: 'div',
22914         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22915         cn: [
22916         {
22917             tag: 'div',
22918             cls: 'datepicker-days',
22919             cn: [
22920             {
22921                 tag: 'table',
22922                 cls: 'table-condensed',
22923                 cn:[
22924                 Roo.bootstrap.DateField.head,
22925                 {
22926                     tag: 'tbody'
22927                 },
22928                 Roo.bootstrap.DateField.footer
22929                 ]
22930             }
22931             ]
22932         },
22933         {
22934             tag: 'div',
22935             cls: 'datepicker-months',
22936             cn: [
22937             {
22938                 tag: 'table',
22939                 cls: 'table-condensed',
22940                 cn:[
22941                 Roo.bootstrap.DateField.head,
22942                 Roo.bootstrap.DateField.content,
22943                 Roo.bootstrap.DateField.footer
22944                 ]
22945             }
22946             ]
22947         },
22948         {
22949             tag: 'div',
22950             cls: 'datepicker-years',
22951             cn: [
22952             {
22953                 tag: 'table',
22954                 cls: 'table-condensed',
22955                 cn:[
22956                 Roo.bootstrap.DateField.head,
22957                 Roo.bootstrap.DateField.content,
22958                 Roo.bootstrap.DateField.footer
22959                 ]
22960             }
22961             ]
22962         }
22963         ]
22964     }
22965 });
22966
22967  
22968
22969  /*
22970  * - LGPL
22971  *
22972  * TimeField
22973  * 
22974  */
22975
22976 /**
22977  * @class Roo.bootstrap.TimeField
22978  * @extends Roo.bootstrap.Input
22979  * Bootstrap DateField class
22980  * 
22981  * 
22982  * @constructor
22983  * Create a new TimeField
22984  * @param {Object} config The config object
22985  */
22986
22987 Roo.bootstrap.TimeField = function(config){
22988     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22989     this.addEvents({
22990             /**
22991              * @event show
22992              * Fires when this field show.
22993              * @param {Roo.bootstrap.DateField} thisthis
22994              * @param {Mixed} date The date value
22995              */
22996             show : true,
22997             /**
22998              * @event show
22999              * Fires when this field hide.
23000              * @param {Roo.bootstrap.DateField} this
23001              * @param {Mixed} date The date value
23002              */
23003             hide : true,
23004             /**
23005              * @event select
23006              * Fires when select a date.
23007              * @param {Roo.bootstrap.DateField} this
23008              * @param {Mixed} date The date value
23009              */
23010             select : true
23011         });
23012 };
23013
23014 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23015     
23016     /**
23017      * @cfg {String} format
23018      * The default time format string which can be overriden for localization support.  The format must be
23019      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23020      */
23021     format : "H:i",
23022
23023     getAutoCreate : function()
23024     {
23025         this.after = '<i class="fa far fa-clock"></i>';
23026         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23027         
23028          
23029     },
23030     onRender: function(ct, position)
23031     {
23032         
23033         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23034                 
23035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23036         
23037         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23038         
23039         this.pop = this.picker().select('>.datepicker-time',true).first();
23040         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23041         
23042         this.picker().on('mousedown', this.onMousedown, this);
23043         this.picker().on('click', this.onClick, this);
23044         
23045         this.picker().addClass('datepicker-dropdown');
23046     
23047         this.fillTime();
23048         this.update();
23049             
23050         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23051         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23052         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23053         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23054         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23055         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23056
23057     },
23058     
23059     fireKey: function(e){
23060         if (!this.picker().isVisible()){
23061             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23062                 this.show();
23063             }
23064             return;
23065         }
23066
23067         e.preventDefault();
23068         
23069         switch(e.keyCode){
23070             case 27: // escape
23071                 this.hide();
23072                 break;
23073             case 37: // left
23074             case 39: // right
23075                 this.onTogglePeriod();
23076                 break;
23077             case 38: // up
23078                 this.onIncrementMinutes();
23079                 break;
23080             case 40: // down
23081                 this.onDecrementMinutes();
23082                 break;
23083             case 13: // enter
23084             case 9: // tab
23085                 this.setTime();
23086                 break;
23087         }
23088     },
23089     
23090     onClick: function(e) {
23091         e.stopPropagation();
23092         e.preventDefault();
23093     },
23094     
23095     picker : function()
23096     {
23097         return this.pickerEl;
23098     },
23099     
23100     fillTime: function()
23101     {    
23102         var time = this.pop.select('tbody', true).first();
23103         
23104         time.dom.innerHTML = '';
23105         
23106         time.createChild({
23107             tag: 'tr',
23108             cn: [
23109                 {
23110                     tag: 'td',
23111                     cn: [
23112                         {
23113                             tag: 'a',
23114                             href: '#',
23115                             cls: 'btn',
23116                             cn: [
23117                                 {
23118                                     tag: 'i',
23119                                     cls: 'hours-up fa fas fa-chevron-up'
23120                                 }
23121                             ]
23122                         } 
23123                     ]
23124                 },
23125                 {
23126                     tag: 'td',
23127                     cls: 'separator'
23128                 },
23129                 {
23130                     tag: 'td',
23131                     cn: [
23132                         {
23133                             tag: 'a',
23134                             href: '#',
23135                             cls: 'btn',
23136                             cn: [
23137                                 {
23138                                     tag: 'i',
23139                                     cls: 'minutes-up fa fas fa-chevron-up'
23140                                 }
23141                             ]
23142                         }
23143                     ]
23144                 },
23145                 {
23146                     tag: 'td',
23147                     cls: 'separator'
23148                 }
23149             ]
23150         });
23151         
23152         time.createChild({
23153             tag: 'tr',
23154             cn: [
23155                 {
23156                     tag: 'td',
23157                     cn: [
23158                         {
23159                             tag: 'span',
23160                             cls: 'timepicker-hour',
23161                             html: '00'
23162                         }  
23163                     ]
23164                 },
23165                 {
23166                     tag: 'td',
23167                     cls: 'separator',
23168                     html: ':'
23169                 },
23170                 {
23171                     tag: 'td',
23172                     cn: [
23173                         {
23174                             tag: 'span',
23175                             cls: 'timepicker-minute',
23176                             html: '00'
23177                         }  
23178                     ]
23179                 },
23180                 {
23181                     tag: 'td',
23182                     cls: 'separator'
23183                 },
23184                 {
23185                     tag: 'td',
23186                     cn: [
23187                         {
23188                             tag: 'button',
23189                             type: 'button',
23190                             cls: 'btn btn-primary period',
23191                             html: 'AM'
23192                             
23193                         }
23194                     ]
23195                 }
23196             ]
23197         });
23198         
23199         time.createChild({
23200             tag: 'tr',
23201             cn: [
23202                 {
23203                     tag: 'td',
23204                     cn: [
23205                         {
23206                             tag: 'a',
23207                             href: '#',
23208                             cls: 'btn',
23209                             cn: [
23210                                 {
23211                                     tag: 'span',
23212                                     cls: 'hours-down fa fas fa-chevron-down'
23213                                 }
23214                             ]
23215                         }
23216                     ]
23217                 },
23218                 {
23219                     tag: 'td',
23220                     cls: 'separator'
23221                 },
23222                 {
23223                     tag: 'td',
23224                     cn: [
23225                         {
23226                             tag: 'a',
23227                             href: '#',
23228                             cls: 'btn',
23229                             cn: [
23230                                 {
23231                                     tag: 'span',
23232                                     cls: 'minutes-down fa fas fa-chevron-down'
23233                                 }
23234                             ]
23235                         }
23236                     ]
23237                 },
23238                 {
23239                     tag: 'td',
23240                     cls: 'separator'
23241                 }
23242             ]
23243         });
23244         
23245     },
23246     
23247     update: function()
23248     {
23249         
23250         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23251         
23252         this.fill();
23253     },
23254     
23255     fill: function() 
23256     {
23257         var hours = this.time.getHours();
23258         var minutes = this.time.getMinutes();
23259         var period = 'AM';
23260         
23261         if(hours > 11){
23262             period = 'PM';
23263         }
23264         
23265         if(hours == 0){
23266             hours = 12;
23267         }
23268         
23269         
23270         if(hours > 12){
23271             hours = hours - 12;
23272         }
23273         
23274         if(hours < 10){
23275             hours = '0' + hours;
23276         }
23277         
23278         if(minutes < 10){
23279             minutes = '0' + minutes;
23280         }
23281         
23282         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23283         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23284         this.pop.select('button', true).first().dom.innerHTML = period;
23285         
23286     },
23287     
23288     place: function()
23289     {   
23290         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23291         
23292         var cls = ['bottom'];
23293         
23294         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23295             cls.pop();
23296             cls.push('top');
23297         }
23298         
23299         cls.push('right');
23300         
23301         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23302             cls.pop();
23303             cls.push('left');
23304         }
23305         //this.picker().setXY(20000,20000);
23306         this.picker().addClass(cls.join('-'));
23307         
23308         var _this = this;
23309         
23310         Roo.each(cls, function(c){
23311             if(c == 'bottom'){
23312                 (function() {
23313                  //  
23314                 }).defer(200);
23315                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23316                 //_this.picker().setTop(_this.inputEl().getHeight());
23317                 return;
23318             }
23319             if(c == 'top'){
23320                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23321                 
23322                 //_this.picker().setTop(0 - _this.picker().getHeight());
23323                 return;
23324             }
23325             /*
23326             if(c == 'left'){
23327                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23328                 return;
23329             }
23330             if(c == 'right'){
23331                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23332                 return;
23333             }
23334             */
23335         });
23336         
23337     },
23338   
23339     onFocus : function()
23340     {
23341         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23342         this.show();
23343     },
23344     
23345     onBlur : function()
23346     {
23347         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23348         this.hide();
23349     },
23350     
23351     show : function()
23352     {
23353         this.picker().show();
23354         this.pop.show();
23355         this.update();
23356         this.place();
23357         
23358         this.fireEvent('show', this, this.date);
23359     },
23360     
23361     hide : function()
23362     {
23363         this.picker().hide();
23364         this.pop.hide();
23365         
23366         this.fireEvent('hide', this, this.date);
23367     },
23368     
23369     setTime : function()
23370     {
23371         this.hide();
23372         this.setValue(this.time.format(this.format));
23373         
23374         this.fireEvent('select', this, this.date);
23375         
23376         
23377     },
23378     
23379     onMousedown: function(e){
23380         e.stopPropagation();
23381         e.preventDefault();
23382     },
23383     
23384     onIncrementHours: function()
23385     {
23386         Roo.log('onIncrementHours');
23387         this.time = this.time.add(Date.HOUR, 1);
23388         this.update();
23389         
23390     },
23391     
23392     onDecrementHours: function()
23393     {
23394         Roo.log('onDecrementHours');
23395         this.time = this.time.add(Date.HOUR, -1);
23396         this.update();
23397     },
23398     
23399     onIncrementMinutes: function()
23400     {
23401         Roo.log('onIncrementMinutes');
23402         this.time = this.time.add(Date.MINUTE, 1);
23403         this.update();
23404     },
23405     
23406     onDecrementMinutes: function()
23407     {
23408         Roo.log('onDecrementMinutes');
23409         this.time = this.time.add(Date.MINUTE, -1);
23410         this.update();
23411     },
23412     
23413     onTogglePeriod: function()
23414     {
23415         Roo.log('onTogglePeriod');
23416         this.time = this.time.add(Date.HOUR, 12);
23417         this.update();
23418     }
23419     
23420    
23421 });
23422  
23423
23424 Roo.apply(Roo.bootstrap.TimeField,  {
23425   
23426     template : {
23427         tag: 'div',
23428         cls: 'datepicker dropdown-menu',
23429         cn: [
23430             {
23431                 tag: 'div',
23432                 cls: 'datepicker-time',
23433                 cn: [
23434                 {
23435                     tag: 'table',
23436                     cls: 'table-condensed',
23437                     cn:[
23438                         {
23439                             tag: 'tbody',
23440                             cn: [
23441                                 {
23442                                     tag: 'tr',
23443                                     cn: [
23444                                     {
23445                                         tag: 'td',
23446                                         colspan: '7'
23447                                     }
23448                                     ]
23449                                 }
23450                             ]
23451                         },
23452                         {
23453                             tag: 'tfoot',
23454                             cn: [
23455                                 {
23456                                     tag: 'tr',
23457                                     cn: [
23458                                     {
23459                                         tag: 'th',
23460                                         colspan: '7',
23461                                         cls: '',
23462                                         cn: [
23463                                             {
23464                                                 tag: 'button',
23465                                                 cls: 'btn btn-info ok',
23466                                                 html: 'OK'
23467                                             }
23468                                         ]
23469                                     }
23470                     
23471                                     ]
23472                                 }
23473                             ]
23474                         }
23475                     ]
23476                 }
23477                 ]
23478             }
23479         ]
23480     }
23481 });
23482
23483  
23484
23485  /*
23486  * - LGPL
23487  *
23488  * MonthField
23489  * 
23490  */
23491
23492 /**
23493  * @class Roo.bootstrap.MonthField
23494  * @extends Roo.bootstrap.Input
23495  * Bootstrap MonthField class
23496  * 
23497  * @cfg {String} language default en
23498  * 
23499  * @constructor
23500  * Create a new MonthField
23501  * @param {Object} config The config object
23502  */
23503
23504 Roo.bootstrap.MonthField = function(config){
23505     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23506     
23507     this.addEvents({
23508         /**
23509          * @event show
23510          * Fires when this field show.
23511          * @param {Roo.bootstrap.MonthField} this
23512          * @param {Mixed} date The date value
23513          */
23514         show : true,
23515         /**
23516          * @event show
23517          * Fires when this field hide.
23518          * @param {Roo.bootstrap.MonthField} this
23519          * @param {Mixed} date The date value
23520          */
23521         hide : true,
23522         /**
23523          * @event select
23524          * Fires when select a date.
23525          * @param {Roo.bootstrap.MonthField} this
23526          * @param {String} oldvalue The old value
23527          * @param {String} newvalue The new value
23528          */
23529         select : true
23530     });
23531 };
23532
23533 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23534     
23535     onRender: function(ct, position)
23536     {
23537         
23538         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23539         
23540         this.language = this.language || 'en';
23541         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23542         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23543         
23544         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23545         this.isInline = false;
23546         this.isInput = true;
23547         this.component = this.el.select('.add-on', true).first() || false;
23548         this.component = (this.component && this.component.length === 0) ? false : this.component;
23549         this.hasInput = this.component && this.inputEL().length;
23550         
23551         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23552         
23553         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23554         
23555         this.picker().on('mousedown', this.onMousedown, this);
23556         this.picker().on('click', this.onClick, this);
23557         
23558         this.picker().addClass('datepicker-dropdown');
23559         
23560         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23561             v.setStyle('width', '189px');
23562         });
23563         
23564         this.fillMonths();
23565         
23566         this.update();
23567         
23568         if(this.isInline) {
23569             this.show();
23570         }
23571         
23572     },
23573     
23574     setValue: function(v, suppressEvent)
23575     {   
23576         var o = this.getValue();
23577         
23578         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23579         
23580         this.update();
23581
23582         if(suppressEvent !== true){
23583             this.fireEvent('select', this, o, v);
23584         }
23585         
23586     },
23587     
23588     getValue: function()
23589     {
23590         return this.value;
23591     },
23592     
23593     onClick: function(e) 
23594     {
23595         e.stopPropagation();
23596         e.preventDefault();
23597         
23598         var target = e.getTarget();
23599         
23600         if(target.nodeName.toLowerCase() === 'i'){
23601             target = Roo.get(target).dom.parentNode;
23602         }
23603         
23604         var nodeName = target.nodeName;
23605         var className = target.className;
23606         var html = target.innerHTML;
23607         
23608         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23609             return;
23610         }
23611         
23612         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23613         
23614         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23615         
23616         this.hide();
23617                         
23618     },
23619     
23620     picker : function()
23621     {
23622         return this.pickerEl;
23623     },
23624     
23625     fillMonths: function()
23626     {    
23627         var i = 0;
23628         var months = this.picker().select('>.datepicker-months td', true).first();
23629         
23630         months.dom.innerHTML = '';
23631         
23632         while (i < 12) {
23633             var month = {
23634                 tag: 'span',
23635                 cls: 'month',
23636                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23637             };
23638             
23639             months.createChild(month);
23640         }
23641         
23642     },
23643     
23644     update: function()
23645     {
23646         var _this = this;
23647         
23648         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23649             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23650         }
23651         
23652         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23653             e.removeClass('active');
23654             
23655             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23656                 e.addClass('active');
23657             }
23658         })
23659     },
23660     
23661     place: function()
23662     {
23663         if(this.isInline) {
23664             return;
23665         }
23666         
23667         this.picker().removeClass(['bottom', 'top']);
23668         
23669         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23670             /*
23671              * place to the top of element!
23672              *
23673              */
23674             
23675             this.picker().addClass('top');
23676             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23677             
23678             return;
23679         }
23680         
23681         this.picker().addClass('bottom');
23682         
23683         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23684     },
23685     
23686     onFocus : function()
23687     {
23688         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23689         this.show();
23690     },
23691     
23692     onBlur : function()
23693     {
23694         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23695         
23696         var d = this.inputEl().getValue();
23697         
23698         this.setValue(d);
23699                 
23700         this.hide();
23701     },
23702     
23703     show : function()
23704     {
23705         this.picker().show();
23706         this.picker().select('>.datepicker-months', true).first().show();
23707         this.update();
23708         this.place();
23709         
23710         this.fireEvent('show', this, this.date);
23711     },
23712     
23713     hide : function()
23714     {
23715         if(this.isInline) {
23716             return;
23717         }
23718         this.picker().hide();
23719         this.fireEvent('hide', this, this.date);
23720         
23721     },
23722     
23723     onMousedown: function(e)
23724     {
23725         e.stopPropagation();
23726         e.preventDefault();
23727     },
23728     
23729     keyup: function(e)
23730     {
23731         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23732         this.update();
23733     },
23734
23735     fireKey: function(e)
23736     {
23737         if (!this.picker().isVisible()){
23738             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23739                 this.show();
23740             }
23741             return;
23742         }
23743         
23744         var dir;
23745         
23746         switch(e.keyCode){
23747             case 27: // escape
23748                 this.hide();
23749                 e.preventDefault();
23750                 break;
23751             case 37: // left
23752             case 39: // right
23753                 dir = e.keyCode == 37 ? -1 : 1;
23754                 
23755                 this.vIndex = this.vIndex + dir;
23756                 
23757                 if(this.vIndex < 0){
23758                     this.vIndex = 0;
23759                 }
23760                 
23761                 if(this.vIndex > 11){
23762                     this.vIndex = 11;
23763                 }
23764                 
23765                 if(isNaN(this.vIndex)){
23766                     this.vIndex = 0;
23767                 }
23768                 
23769                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23770                 
23771                 break;
23772             case 38: // up
23773             case 40: // down
23774                 
23775                 dir = e.keyCode == 38 ? -1 : 1;
23776                 
23777                 this.vIndex = this.vIndex + dir * 4;
23778                 
23779                 if(this.vIndex < 0){
23780                     this.vIndex = 0;
23781                 }
23782                 
23783                 if(this.vIndex > 11){
23784                     this.vIndex = 11;
23785                 }
23786                 
23787                 if(isNaN(this.vIndex)){
23788                     this.vIndex = 0;
23789                 }
23790                 
23791                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23792                 break;
23793                 
23794             case 13: // enter
23795                 
23796                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23797                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23798                 }
23799                 
23800                 this.hide();
23801                 e.preventDefault();
23802                 break;
23803             case 9: // tab
23804                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23805                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23806                 }
23807                 this.hide();
23808                 break;
23809             case 16: // shift
23810             case 17: // ctrl
23811             case 18: // alt
23812                 break;
23813             default :
23814                 this.hide();
23815                 
23816         }
23817     },
23818     
23819     remove: function() 
23820     {
23821         this.picker().remove();
23822     }
23823    
23824 });
23825
23826 Roo.apply(Roo.bootstrap.MonthField,  {
23827     
23828     content : {
23829         tag: 'tbody',
23830         cn: [
23831         {
23832             tag: 'tr',
23833             cn: [
23834             {
23835                 tag: 'td',
23836                 colspan: '7'
23837             }
23838             ]
23839         }
23840         ]
23841     },
23842     
23843     dates:{
23844         en: {
23845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23847         }
23848     }
23849 });
23850
23851 Roo.apply(Roo.bootstrap.MonthField,  {
23852   
23853     template : {
23854         tag: 'div',
23855         cls: 'datepicker dropdown-menu roo-dynamic',
23856         cn: [
23857             {
23858                 tag: 'div',
23859                 cls: 'datepicker-months',
23860                 cn: [
23861                 {
23862                     tag: 'table',
23863                     cls: 'table-condensed',
23864                     cn:[
23865                         Roo.bootstrap.DateField.content
23866                     ]
23867                 }
23868                 ]
23869             }
23870         ]
23871     }
23872 });
23873
23874  
23875
23876  
23877  /*
23878  * - LGPL
23879  *
23880  * CheckBox
23881  * 
23882  */
23883
23884 /**
23885  * @class Roo.bootstrap.CheckBox
23886  * @extends Roo.bootstrap.Input
23887  * Bootstrap CheckBox class
23888  * 
23889  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23890  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23891  * @cfg {String} boxLabel The text that appears beside the checkbox
23892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23893  * @cfg {Boolean} checked initnal the element
23894  * @cfg {Boolean} inline inline the element (default false)
23895  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23896  * @cfg {String} tooltip label tooltip
23897  * 
23898  * @constructor
23899  * Create a new CheckBox
23900  * @param {Object} config The config object
23901  */
23902
23903 Roo.bootstrap.CheckBox = function(config){
23904     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23905    
23906     this.addEvents({
23907         /**
23908         * @event check
23909         * Fires when the element is checked or unchecked.
23910         * @param {Roo.bootstrap.CheckBox} this This input
23911         * @param {Boolean} checked The new checked value
23912         */
23913        check : true,
23914        /**
23915         * @event click
23916         * Fires when the element is click.
23917         * @param {Roo.bootstrap.CheckBox} this This input
23918         */
23919        click : true
23920     });
23921     
23922 };
23923
23924 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23925   
23926     inputType: 'checkbox',
23927     inputValue: 1,
23928     valueOff: 0,
23929     boxLabel: false,
23930     checked: false,
23931     weight : false,
23932     inline: false,
23933     tooltip : '',
23934     
23935     // checkbox success does not make any sense really.. 
23936     invalidClass : "",
23937     validClass : "",
23938     
23939     
23940     getAutoCreate : function()
23941     {
23942         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23943         
23944         var id = Roo.id();
23945         
23946         var cfg = {};
23947         
23948         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23949         
23950         if(this.inline){
23951             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23952         }
23953         
23954         var input =  {
23955             tag: 'input',
23956             id : id,
23957             type : this.inputType,
23958             value : this.inputValue,
23959             cls : 'roo-' + this.inputType, //'form-box',
23960             placeholder : this.placeholder || ''
23961             
23962         };
23963         
23964         if(this.inputType != 'radio'){
23965             var hidden =  {
23966                 tag: 'input',
23967                 type : 'hidden',
23968                 cls : 'roo-hidden-value',
23969                 value : this.checked ? this.inputValue : this.valueOff
23970             };
23971         }
23972         
23973             
23974         if (this.weight) { // Validity check?
23975             cfg.cls += " " + this.inputType + "-" + this.weight;
23976         }
23977         
23978         if (this.disabled) {
23979             input.disabled=true;
23980         }
23981         
23982         if(this.checked){
23983             input.checked = this.checked;
23984         }
23985         
23986         if (this.name) {
23987             
23988             input.name = this.name;
23989             
23990             if(this.inputType != 'radio'){
23991                 hidden.name = this.name;
23992                 input.name = '_hidden_' + this.name;
23993             }
23994         }
23995         
23996         if (this.size) {
23997             input.cls += ' input-' + this.size;
23998         }
23999         
24000         var settings=this;
24001         
24002         ['xs','sm','md','lg'].map(function(size){
24003             if (settings[size]) {
24004                 cfg.cls += ' col-' + size + '-' + settings[size];
24005             }
24006         });
24007         
24008         var inputblock = input;
24009          
24010         if (this.before || this.after) {
24011             
24012             inputblock = {
24013                 cls : 'input-group',
24014                 cn :  [] 
24015             };
24016             
24017             if (this.before) {
24018                 inputblock.cn.push({
24019                     tag :'span',
24020                     cls : 'input-group-addon',
24021                     html : this.before
24022                 });
24023             }
24024             
24025             inputblock.cn.push(input);
24026             
24027             if(this.inputType != 'radio'){
24028                 inputblock.cn.push(hidden);
24029             }
24030             
24031             if (this.after) {
24032                 inputblock.cn.push({
24033                     tag :'span',
24034                     cls : 'input-group-addon',
24035                     html : this.after
24036                 });
24037             }
24038             
24039         }
24040         var boxLabelCfg = false;
24041         
24042         if(this.boxLabel){
24043            
24044             boxLabelCfg = {
24045                 tag: 'label',
24046                 //'for': id, // box label is handled by onclick - so no for...
24047                 cls: 'box-label',
24048                 html: this.boxLabel
24049             };
24050             if(this.tooltip){
24051                 boxLabelCfg.tooltip = this.tooltip;
24052             }
24053              
24054         }
24055         
24056         
24057         if (align ==='left' && this.fieldLabel.length) {
24058 //                Roo.log("left and has label");
24059             cfg.cn = [
24060                 {
24061                     tag: 'label',
24062                     'for' :  id,
24063                     cls : 'control-label',
24064                     html : this.fieldLabel
24065                 },
24066                 {
24067                     cls : "", 
24068                     cn: [
24069                         inputblock
24070                     ]
24071                 }
24072             ];
24073             
24074             if (boxLabelCfg) {
24075                 cfg.cn[1].cn.push(boxLabelCfg);
24076             }
24077             
24078             if(this.labelWidth > 12){
24079                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24080             }
24081             
24082             if(this.labelWidth < 13 && this.labelmd == 0){
24083                 this.labelmd = this.labelWidth;
24084             }
24085             
24086             if(this.labellg > 0){
24087                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24088                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24089             }
24090             
24091             if(this.labelmd > 0){
24092                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24093                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24094             }
24095             
24096             if(this.labelsm > 0){
24097                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24098                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24099             }
24100             
24101             if(this.labelxs > 0){
24102                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24103                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24104             }
24105             
24106         } else if ( this.fieldLabel.length) {
24107 //                Roo.log(" label");
24108                 cfg.cn = [
24109                    
24110                     {
24111                         tag: this.boxLabel ? 'span' : 'label',
24112                         'for': id,
24113                         cls: 'control-label box-input-label',
24114                         //cls : 'input-group-addon',
24115                         html : this.fieldLabel
24116                     },
24117                     
24118                     inputblock
24119                     
24120                 ];
24121                 if (boxLabelCfg) {
24122                     cfg.cn.push(boxLabelCfg);
24123                 }
24124
24125         } else {
24126             
24127 //                Roo.log(" no label && no align");
24128                 cfg.cn = [  inputblock ] ;
24129                 if (boxLabelCfg) {
24130                     cfg.cn.push(boxLabelCfg);
24131                 }
24132
24133                 
24134         }
24135         
24136        
24137         
24138         if(this.inputType != 'radio'){
24139             cfg.cn.push(hidden);
24140         }
24141         
24142         return cfg;
24143         
24144     },
24145     
24146     /**
24147      * return the real input element.
24148      */
24149     inputEl: function ()
24150     {
24151         return this.el.select('input.roo-' + this.inputType,true).first();
24152     },
24153     hiddenEl: function ()
24154     {
24155         return this.el.select('input.roo-hidden-value',true).first();
24156     },
24157     
24158     labelEl: function()
24159     {
24160         return this.el.select('label.control-label',true).first();
24161     },
24162     /* depricated... */
24163     
24164     label: function()
24165     {
24166         return this.labelEl();
24167     },
24168     
24169     boxLabelEl: function()
24170     {
24171         return this.el.select('label.box-label',true).first();
24172     },
24173     
24174     initEvents : function()
24175     {
24176 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24177         
24178         this.inputEl().on('click', this.onClick,  this);
24179         
24180         if (this.boxLabel) { 
24181             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24182         }
24183         
24184         this.startValue = this.getValue();
24185         
24186         if(this.groupId){
24187             Roo.bootstrap.CheckBox.register(this);
24188         }
24189     },
24190     
24191     onClick : function(e)
24192     {   
24193         if(this.fireEvent('click', this, e) !== false){
24194             this.setChecked(!this.checked);
24195         }
24196         
24197     },
24198     
24199     setChecked : function(state,suppressEvent)
24200     {
24201         this.startValue = this.getValue();
24202
24203         if(this.inputType == 'radio'){
24204             
24205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24206                 e.dom.checked = false;
24207             });
24208             
24209             this.inputEl().dom.checked = true;
24210             
24211             this.inputEl().dom.value = this.inputValue;
24212             
24213             if(suppressEvent !== true){
24214                 this.fireEvent('check', this, true);
24215             }
24216             
24217             this.validate();
24218             
24219             return;
24220         }
24221         
24222         this.checked = state;
24223         
24224         this.inputEl().dom.checked = state;
24225         
24226         
24227         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24228         
24229         if(suppressEvent !== true){
24230             this.fireEvent('check', this, state);
24231         }
24232         
24233         this.validate();
24234     },
24235     
24236     getValue : function()
24237     {
24238         if(this.inputType == 'radio'){
24239             return this.getGroupValue();
24240         }
24241         
24242         return this.hiddenEl().dom.value;
24243         
24244     },
24245     
24246     getGroupValue : function()
24247     {
24248         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24249             return '';
24250         }
24251         
24252         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24253     },
24254     
24255     setValue : function(v,suppressEvent)
24256     {
24257         if(this.inputType == 'radio'){
24258             this.setGroupValue(v, suppressEvent);
24259             return;
24260         }
24261         
24262         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24263         
24264         this.validate();
24265     },
24266     
24267     setGroupValue : function(v, suppressEvent)
24268     {
24269         this.startValue = this.getValue();
24270         
24271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24272             e.dom.checked = false;
24273             
24274             if(e.dom.value == v){
24275                 e.dom.checked = true;
24276             }
24277         });
24278         
24279         if(suppressEvent !== true){
24280             this.fireEvent('check', this, true);
24281         }
24282
24283         this.validate();
24284         
24285         return;
24286     },
24287     
24288     validate : function()
24289     {
24290         if(this.getVisibilityEl().hasClass('hidden')){
24291             return true;
24292         }
24293         
24294         if(
24295                 this.disabled || 
24296                 (this.inputType == 'radio' && this.validateRadio()) ||
24297                 (this.inputType == 'checkbox' && this.validateCheckbox())
24298         ){
24299             this.markValid();
24300             return true;
24301         }
24302         
24303         this.markInvalid();
24304         return false;
24305     },
24306     
24307     validateRadio : function()
24308     {
24309         if(this.getVisibilityEl().hasClass('hidden')){
24310             return true;
24311         }
24312         
24313         if(this.allowBlank){
24314             return true;
24315         }
24316         
24317         var valid = false;
24318         
24319         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24320             if(!e.dom.checked){
24321                 return;
24322             }
24323             
24324             valid = true;
24325             
24326             return false;
24327         });
24328         
24329         return valid;
24330     },
24331     
24332     validateCheckbox : function()
24333     {
24334         if(!this.groupId){
24335             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24336             //return (this.getValue() == this.inputValue) ? true : false;
24337         }
24338         
24339         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24340         
24341         if(!group){
24342             return false;
24343         }
24344         
24345         var r = false;
24346         
24347         for(var i in group){
24348             if(group[i].el.isVisible(true)){
24349                 r = false;
24350                 break;
24351             }
24352             
24353             r = true;
24354         }
24355         
24356         for(var i in group){
24357             if(r){
24358                 break;
24359             }
24360             
24361             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24362         }
24363         
24364         return r;
24365     },
24366     
24367     /**
24368      * Mark this field as valid
24369      */
24370     markValid : function()
24371     {
24372         var _this = this;
24373         
24374         this.fireEvent('valid', this);
24375         
24376         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24377         
24378         if(this.groupId){
24379             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24380         }
24381         
24382         if(label){
24383             label.markValid();
24384         }
24385
24386         if(this.inputType == 'radio'){
24387             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24388                 var fg = e.findParent('.form-group', false, true);
24389                 if (Roo.bootstrap.version == 3) {
24390                     fg.removeClass([_this.invalidClass, _this.validClass]);
24391                     fg.addClass(_this.validClass);
24392                 } else {
24393                     fg.removeClass(['is-valid', 'is-invalid']);
24394                     fg.addClass('is-valid');
24395                 }
24396             });
24397             
24398             return;
24399         }
24400
24401         if(!this.groupId){
24402             var fg = this.el.findParent('.form-group', false, true);
24403             if (Roo.bootstrap.version == 3) {
24404                 fg.removeClass([this.invalidClass, this.validClass]);
24405                 fg.addClass(this.validClass);
24406             } else {
24407                 fg.removeClass(['is-valid', 'is-invalid']);
24408                 fg.addClass('is-valid');
24409             }
24410             return;
24411         }
24412         
24413         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24414         
24415         if(!group){
24416             return;
24417         }
24418         
24419         for(var i in group){
24420             var fg = group[i].el.findParent('.form-group', false, true);
24421             if (Roo.bootstrap.version == 3) {
24422                 fg.removeClass([this.invalidClass, this.validClass]);
24423                 fg.addClass(this.validClass);
24424             } else {
24425                 fg.removeClass(['is-valid', 'is-invalid']);
24426                 fg.addClass('is-valid');
24427             }
24428         }
24429     },
24430     
24431      /**
24432      * Mark this field as invalid
24433      * @param {String} msg The validation message
24434      */
24435     markInvalid : function(msg)
24436     {
24437         if(this.allowBlank){
24438             return;
24439         }
24440         
24441         var _this = this;
24442         
24443         this.fireEvent('invalid', this, msg);
24444         
24445         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24446         
24447         if(this.groupId){
24448             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24449         }
24450         
24451         if(label){
24452             label.markInvalid();
24453         }
24454             
24455         if(this.inputType == 'radio'){
24456             
24457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24458                 var fg = e.findParent('.form-group', false, true);
24459                 if (Roo.bootstrap.version == 3) {
24460                     fg.removeClass([_this.invalidClass, _this.validClass]);
24461                     fg.addClass(_this.invalidClass);
24462                 } else {
24463                     fg.removeClass(['is-invalid', 'is-valid']);
24464                     fg.addClass('is-invalid');
24465                 }
24466             });
24467             
24468             return;
24469         }
24470         
24471         if(!this.groupId){
24472             var fg = this.el.findParent('.form-group', false, true);
24473             if (Roo.bootstrap.version == 3) {
24474                 fg.removeClass([_this.invalidClass, _this.validClass]);
24475                 fg.addClass(_this.invalidClass);
24476             } else {
24477                 fg.removeClass(['is-invalid', 'is-valid']);
24478                 fg.addClass('is-invalid');
24479             }
24480             return;
24481         }
24482         
24483         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24484         
24485         if(!group){
24486             return;
24487         }
24488         
24489         for(var i in group){
24490             var fg = group[i].el.findParent('.form-group', false, true);
24491             if (Roo.bootstrap.version == 3) {
24492                 fg.removeClass([_this.invalidClass, _this.validClass]);
24493                 fg.addClass(_this.invalidClass);
24494             } else {
24495                 fg.removeClass(['is-invalid', 'is-valid']);
24496                 fg.addClass('is-invalid');
24497             }
24498         }
24499         
24500     },
24501     
24502     clearInvalid : function()
24503     {
24504         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24505         
24506         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24507         
24508         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24509         
24510         if (label && label.iconEl) {
24511             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24512             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24513         }
24514     },
24515     
24516     disable : function()
24517     {
24518         if(this.inputType != 'radio'){
24519             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24520             return;
24521         }
24522         
24523         var _this = this;
24524         
24525         if(this.rendered){
24526             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24527                 _this.getActionEl().addClass(this.disabledClass);
24528                 e.dom.disabled = true;
24529             });
24530         }
24531         
24532         this.disabled = true;
24533         this.fireEvent("disable", this);
24534         return this;
24535     },
24536
24537     enable : function()
24538     {
24539         if(this.inputType != 'radio'){
24540             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24541             return;
24542         }
24543         
24544         var _this = this;
24545         
24546         if(this.rendered){
24547             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24548                 _this.getActionEl().removeClass(this.disabledClass);
24549                 e.dom.disabled = false;
24550             });
24551         }
24552         
24553         this.disabled = false;
24554         this.fireEvent("enable", this);
24555         return this;
24556     },
24557     
24558     setBoxLabel : function(v)
24559     {
24560         this.boxLabel = v;
24561         
24562         if(this.rendered){
24563             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24564         }
24565     }
24566
24567 });
24568
24569 Roo.apply(Roo.bootstrap.CheckBox, {
24570     
24571     groups: {},
24572     
24573      /**
24574     * register a CheckBox Group
24575     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24576     */
24577     register : function(checkbox)
24578     {
24579         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24580             this.groups[checkbox.groupId] = {};
24581         }
24582         
24583         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24584             return;
24585         }
24586         
24587         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24588         
24589     },
24590     /**
24591     * fetch a CheckBox Group based on the group ID
24592     * @param {string} the group ID
24593     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24594     */
24595     get: function(groupId) {
24596         if (typeof(this.groups[groupId]) == 'undefined') {
24597             return false;
24598         }
24599         
24600         return this.groups[groupId] ;
24601     }
24602     
24603     
24604 });
24605 /*
24606  * - LGPL
24607  *
24608  * RadioItem
24609  * 
24610  */
24611
24612 /**
24613  * @class Roo.bootstrap.Radio
24614  * @extends Roo.bootstrap.Component
24615  * Bootstrap Radio class
24616  * @cfg {String} boxLabel - the label associated
24617  * @cfg {String} value - the value of radio
24618  * 
24619  * @constructor
24620  * Create a new Radio
24621  * @param {Object} config The config object
24622  */
24623 Roo.bootstrap.Radio = function(config){
24624     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24625     
24626 };
24627
24628 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24629     
24630     boxLabel : '',
24631     
24632     value : '',
24633     
24634     getAutoCreate : function()
24635     {
24636         var cfg = {
24637             tag : 'div',
24638             cls : 'form-group radio',
24639             cn : [
24640                 {
24641                     tag : 'label',
24642                     cls : 'box-label',
24643                     html : this.boxLabel
24644                 }
24645             ]
24646         };
24647         
24648         return cfg;
24649     },
24650     
24651     initEvents : function() 
24652     {
24653         this.parent().register(this);
24654         
24655         this.el.on('click', this.onClick, this);
24656         
24657     },
24658     
24659     onClick : function(e)
24660     {
24661         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24662             this.setChecked(true);
24663         }
24664     },
24665     
24666     setChecked : function(state, suppressEvent)
24667     {
24668         this.parent().setValue(this.value, suppressEvent);
24669         
24670     },
24671     
24672     setBoxLabel : function(v)
24673     {
24674         this.boxLabel = v;
24675         
24676         if(this.rendered){
24677             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24678         }
24679     }
24680     
24681 });
24682  
24683
24684  /*
24685  * - LGPL
24686  *
24687  * Input
24688  * 
24689  */
24690
24691 /**
24692  * @class Roo.bootstrap.SecurePass
24693  * @extends Roo.bootstrap.Input
24694  * Bootstrap SecurePass class
24695  *
24696  * 
24697  * @constructor
24698  * Create a new SecurePass
24699  * @param {Object} config The config object
24700  */
24701  
24702 Roo.bootstrap.SecurePass = function (config) {
24703     // these go here, so the translation tool can replace them..
24704     this.errors = {
24705         PwdEmpty: "Please type a password, and then retype it to confirm.",
24706         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24707         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24708         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24709         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24710         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24711         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24712         TooWeak: "Your password is Too Weak."
24713     },
24714     this.meterLabel = "Password strength:";
24715     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24716     this.meterClass = [
24717         "roo-password-meter-tooweak", 
24718         "roo-password-meter-weak", 
24719         "roo-password-meter-medium", 
24720         "roo-password-meter-strong", 
24721         "roo-password-meter-grey"
24722     ];
24723     
24724     this.errors = {};
24725     
24726     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24727 }
24728
24729 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24730     /**
24731      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24732      * {
24733      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24734      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24735      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24736      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24737      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24738      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24739      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24740      * })
24741      */
24742     // private
24743     
24744     meterWidth: 300,
24745     errorMsg :'',    
24746     errors: false,
24747     imageRoot: '/',
24748     /**
24749      * @cfg {String/Object} Label for the strength meter (defaults to
24750      * 'Password strength:')
24751      */
24752     // private
24753     meterLabel: '',
24754     /**
24755      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24756      * ['Weak', 'Medium', 'Strong'])
24757      */
24758     // private    
24759     pwdStrengths: false,    
24760     // private
24761     strength: 0,
24762     // private
24763     _lastPwd: null,
24764     // private
24765     kCapitalLetter: 0,
24766     kSmallLetter: 1,
24767     kDigit: 2,
24768     kPunctuation: 3,
24769     
24770     insecure: false,
24771     // private
24772     initEvents: function ()
24773     {
24774         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24775
24776         if (this.el.is('input[type=password]') && Roo.isSafari) {
24777             this.el.on('keydown', this.SafariOnKeyDown, this);
24778         }
24779
24780         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24781     },
24782     // private
24783     onRender: function (ct, position)
24784     {
24785         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24786         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24787         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24788
24789         this.trigger.createChild({
24790                    cn: [
24791                     {
24792                     //id: 'PwdMeter',
24793                     tag: 'div',
24794                     cls: 'roo-password-meter-grey col-xs-12',
24795                     style: {
24796                         //width: 0,
24797                         //width: this.meterWidth + 'px'                                                
24798                         }
24799                     },
24800                     {                            
24801                          cls: 'roo-password-meter-text'                          
24802                     }
24803                 ]            
24804         });
24805
24806          
24807         if (this.hideTrigger) {
24808             this.trigger.setDisplayed(false);
24809         }
24810         this.setSize(this.width || '', this.height || '');
24811     },
24812     // private
24813     onDestroy: function ()
24814     {
24815         if (this.trigger) {
24816             this.trigger.removeAllListeners();
24817             this.trigger.remove();
24818         }
24819         if (this.wrap) {
24820             this.wrap.remove();
24821         }
24822         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24823     },
24824     // private
24825     checkStrength: function ()
24826     {
24827         var pwd = this.inputEl().getValue();
24828         if (pwd == this._lastPwd) {
24829             return;
24830         }
24831
24832         var strength;
24833         if (this.ClientSideStrongPassword(pwd)) {
24834             strength = 3;
24835         } else if (this.ClientSideMediumPassword(pwd)) {
24836             strength = 2;
24837         } else if (this.ClientSideWeakPassword(pwd)) {
24838             strength = 1;
24839         } else {
24840             strength = 0;
24841         }
24842         
24843         Roo.log('strength1: ' + strength);
24844         
24845         //var pm = this.trigger.child('div/div/div').dom;
24846         var pm = this.trigger.child('div/div');
24847         pm.removeClass(this.meterClass);
24848         pm.addClass(this.meterClass[strength]);
24849                 
24850         
24851         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24852                 
24853         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24854         
24855         this._lastPwd = pwd;
24856     },
24857     reset: function ()
24858     {
24859         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24860         
24861         this._lastPwd = '';
24862         
24863         var pm = this.trigger.child('div/div');
24864         pm.removeClass(this.meterClass);
24865         pm.addClass('roo-password-meter-grey');        
24866         
24867         
24868         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24869         
24870         pt.innerHTML = '';
24871         this.inputEl().dom.type='password';
24872     },
24873     // private
24874     validateValue: function (value)
24875     {
24876         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24877             return false;
24878         }
24879         if (value.length == 0) {
24880             if (this.allowBlank) {
24881                 this.clearInvalid();
24882                 return true;
24883             }
24884
24885             this.markInvalid(this.errors.PwdEmpty);
24886             this.errorMsg = this.errors.PwdEmpty;
24887             return false;
24888         }
24889         
24890         if(this.insecure){
24891             return true;
24892         }
24893         
24894         if (!value.match(/[\x21-\x7e]+/)) {
24895             this.markInvalid(this.errors.PwdBadChar);
24896             this.errorMsg = this.errors.PwdBadChar;
24897             return false;
24898         }
24899         if (value.length < 6) {
24900             this.markInvalid(this.errors.PwdShort);
24901             this.errorMsg = this.errors.PwdShort;
24902             return false;
24903         }
24904         if (value.length > 16) {
24905             this.markInvalid(this.errors.PwdLong);
24906             this.errorMsg = this.errors.PwdLong;
24907             return false;
24908         }
24909         var strength;
24910         if (this.ClientSideStrongPassword(value)) {
24911             strength = 3;
24912         } else if (this.ClientSideMediumPassword(value)) {
24913             strength = 2;
24914         } else if (this.ClientSideWeakPassword(value)) {
24915             strength = 1;
24916         } else {
24917             strength = 0;
24918         }
24919
24920         
24921         if (strength < 2) {
24922             //this.markInvalid(this.errors.TooWeak);
24923             this.errorMsg = this.errors.TooWeak;
24924             //return false;
24925         }
24926         
24927         
24928         console.log('strength2: ' + strength);
24929         
24930         //var pm = this.trigger.child('div/div/div').dom;
24931         
24932         var pm = this.trigger.child('div/div');
24933         pm.removeClass(this.meterClass);
24934         pm.addClass(this.meterClass[strength]);
24935                 
24936         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24937                 
24938         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24939         
24940         this.errorMsg = ''; 
24941         return true;
24942     },
24943     // private
24944     CharacterSetChecks: function (type)
24945     {
24946         this.type = type;
24947         this.fResult = false;
24948     },
24949     // private
24950     isctype: function (character, type)
24951     {
24952         switch (type) {  
24953             case this.kCapitalLetter:
24954                 if (character >= 'A' && character <= 'Z') {
24955                     return true;
24956                 }
24957                 break;
24958             
24959             case this.kSmallLetter:
24960                 if (character >= 'a' && character <= 'z') {
24961                     return true;
24962                 }
24963                 break;
24964             
24965             case this.kDigit:
24966                 if (character >= '0' && character <= '9') {
24967                     return true;
24968                 }
24969                 break;
24970             
24971             case this.kPunctuation:
24972                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24973                     return true;
24974                 }
24975                 break;
24976             
24977             default:
24978                 return false;
24979         }
24980
24981     },
24982     // private
24983     IsLongEnough: function (pwd, size)
24984     {
24985         return !(pwd == null || isNaN(size) || pwd.length < size);
24986     },
24987     // private
24988     SpansEnoughCharacterSets: function (word, nb)
24989     {
24990         if (!this.IsLongEnough(word, nb))
24991         {
24992             return false;
24993         }
24994
24995         var characterSetChecks = new Array(
24996             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24997             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24998         );
24999         
25000         for (var index = 0; index < word.length; ++index) {
25001             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25002                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25003                     characterSetChecks[nCharSet].fResult = true;
25004                     break;
25005                 }
25006             }
25007         }
25008
25009         var nCharSets = 0;
25010         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25011             if (characterSetChecks[nCharSet].fResult) {
25012                 ++nCharSets;
25013             }
25014         }
25015
25016         if (nCharSets < nb) {
25017             return false;
25018         }
25019         return true;
25020     },
25021     // private
25022     ClientSideStrongPassword: function (pwd)
25023     {
25024         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25025     },
25026     // private
25027     ClientSideMediumPassword: function (pwd)
25028     {
25029         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25030     },
25031     // private
25032     ClientSideWeakPassword: function (pwd)
25033     {
25034         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25035     }
25036           
25037 })//<script type="text/javascript">
25038
25039 /*
25040  * Based  Ext JS Library 1.1.1
25041  * Copyright(c) 2006-2007, Ext JS, LLC.
25042  * LGPL
25043  *
25044  */
25045  
25046 /**
25047  * @class Roo.HtmlEditorCore
25048  * @extends Roo.Component
25049  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25050  *
25051  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25052  */
25053
25054 Roo.HtmlEditorCore = function(config){
25055     
25056     
25057     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25058     
25059     
25060     this.addEvents({
25061         /**
25062          * @event initialize
25063          * Fires when the editor is fully initialized (including the iframe)
25064          * @param {Roo.HtmlEditorCore} this
25065          */
25066         initialize: true,
25067         /**
25068          * @event activate
25069          * Fires when the editor is first receives the focus. Any insertion must wait
25070          * until after this event.
25071          * @param {Roo.HtmlEditorCore} this
25072          */
25073         activate: true,
25074          /**
25075          * @event beforesync
25076          * Fires before the textarea is updated with content from the editor iframe. Return false
25077          * to cancel the sync.
25078          * @param {Roo.HtmlEditorCore} this
25079          * @param {String} html
25080          */
25081         beforesync: true,
25082          /**
25083          * @event beforepush
25084          * Fires before the iframe editor is updated with content from the textarea. Return false
25085          * to cancel the push.
25086          * @param {Roo.HtmlEditorCore} this
25087          * @param {String} html
25088          */
25089         beforepush: true,
25090          /**
25091          * @event sync
25092          * Fires when the textarea is updated with content from the editor iframe.
25093          * @param {Roo.HtmlEditorCore} this
25094          * @param {String} html
25095          */
25096         sync: true,
25097          /**
25098          * @event push
25099          * Fires when the iframe editor is updated with content from the textarea.
25100          * @param {Roo.HtmlEditorCore} this
25101          * @param {String} html
25102          */
25103         push: true,
25104         
25105         /**
25106          * @event editorevent
25107          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25108          * @param {Roo.HtmlEditorCore} this
25109          */
25110         editorevent: true
25111         
25112     });
25113     
25114     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25115     
25116     // defaults : white / black...
25117     this.applyBlacklists();
25118     
25119     
25120     
25121 };
25122
25123
25124 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25125
25126
25127      /**
25128      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25129      */
25130     
25131     owner : false,
25132     
25133      /**
25134      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25135      *                        Roo.resizable.
25136      */
25137     resizable : false,
25138      /**
25139      * @cfg {Number} height (in pixels)
25140      */   
25141     height: 300,
25142    /**
25143      * @cfg {Number} width (in pixels)
25144      */   
25145     width: 500,
25146     
25147     /**
25148      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25149      * 
25150      */
25151     stylesheets: false,
25152     
25153     // id of frame..
25154     frameId: false,
25155     
25156     // private properties
25157     validationEvent : false,
25158     deferHeight: true,
25159     initialized : false,
25160     activated : false,
25161     sourceEditMode : false,
25162     onFocus : Roo.emptyFn,
25163     iframePad:3,
25164     hideMode:'offsets',
25165     
25166     clearUp: true,
25167     
25168     // blacklist + whitelisted elements..
25169     black: false,
25170     white: false,
25171      
25172     bodyCls : '',
25173
25174     /**
25175      * Protected method that will not generally be called directly. It
25176      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25177      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25178      */
25179     getDocMarkup : function(){
25180         // body styles..
25181         var st = '';
25182         
25183         // inherit styels from page...?? 
25184         if (this.stylesheets === false) {
25185             
25186             Roo.get(document.head).select('style').each(function(node) {
25187                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25188             });
25189             
25190             Roo.get(document.head).select('link').each(function(node) { 
25191                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25192             });
25193             
25194         } else if (!this.stylesheets.length) {
25195                 // simple..
25196                 st = '<style type="text/css">' +
25197                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25198                    '</style>';
25199         } else {
25200             for (var i in this.stylesheets) { 
25201                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25202             }
25203             
25204         }
25205         
25206         st +=  '<style type="text/css">' +
25207             'IMG { cursor: pointer } ' +
25208         '</style>';
25209
25210         var cls = 'roo-htmleditor-body';
25211         
25212         if(this.bodyCls.length){
25213             cls += ' ' + this.bodyCls;
25214         }
25215         
25216         return '<html><head>' + st  +
25217             //<style type="text/css">' +
25218             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25219             //'</style>' +
25220             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25221     },
25222
25223     // private
25224     onRender : function(ct, position)
25225     {
25226         var _t = this;
25227         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25228         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25229         
25230         
25231         this.el.dom.style.border = '0 none';
25232         this.el.dom.setAttribute('tabIndex', -1);
25233         this.el.addClass('x-hidden hide');
25234         
25235         
25236         
25237         if(Roo.isIE){ // fix IE 1px bogus margin
25238             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25239         }
25240        
25241         
25242         this.frameId = Roo.id();
25243         
25244          
25245         
25246         var iframe = this.owner.wrap.createChild({
25247             tag: 'iframe',
25248             cls: 'form-control', // bootstrap..
25249             id: this.frameId,
25250             name: this.frameId,
25251             frameBorder : 'no',
25252             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25253         }, this.el
25254         );
25255         
25256         
25257         this.iframe = iframe.dom;
25258
25259          this.assignDocWin();
25260         
25261         this.doc.designMode = 'on';
25262        
25263         this.doc.open();
25264         this.doc.write(this.getDocMarkup());
25265         this.doc.close();
25266
25267         
25268         var task = { // must defer to wait for browser to be ready
25269             run : function(){
25270                 //console.log("run task?" + this.doc.readyState);
25271                 this.assignDocWin();
25272                 if(this.doc.body || this.doc.readyState == 'complete'){
25273                     try {
25274                         this.doc.designMode="on";
25275                     } catch (e) {
25276                         return;
25277                     }
25278                     Roo.TaskMgr.stop(task);
25279                     this.initEditor.defer(10, this);
25280                 }
25281             },
25282             interval : 10,
25283             duration: 10000,
25284             scope: this
25285         };
25286         Roo.TaskMgr.start(task);
25287
25288     },
25289
25290     // private
25291     onResize : function(w, h)
25292     {
25293          Roo.log('resize: ' +w + ',' + h );
25294         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25295         if(!this.iframe){
25296             return;
25297         }
25298         if(typeof w == 'number'){
25299             
25300             this.iframe.style.width = w + 'px';
25301         }
25302         if(typeof h == 'number'){
25303             
25304             this.iframe.style.height = h + 'px';
25305             if(this.doc){
25306                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25307             }
25308         }
25309         
25310     },
25311
25312     /**
25313      * Toggles the editor between standard and source edit mode.
25314      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25315      */
25316     toggleSourceEdit : function(sourceEditMode){
25317         
25318         this.sourceEditMode = sourceEditMode === true;
25319         
25320         if(this.sourceEditMode){
25321  
25322             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25323             
25324         }else{
25325             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25326             //this.iframe.className = '';
25327             this.deferFocus();
25328         }
25329         //this.setSize(this.owner.wrap.getSize());
25330         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25331     },
25332
25333     
25334   
25335
25336     /**
25337      * Protected method that will not generally be called directly. If you need/want
25338      * custom HTML cleanup, this is the method you should override.
25339      * @param {String} html The HTML to be cleaned
25340      * return {String} The cleaned HTML
25341      */
25342     cleanHtml : function(html){
25343         html = String(html);
25344         if(html.length > 5){
25345             if(Roo.isSafari){ // strip safari nonsense
25346                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25347             }
25348         }
25349         if(html == '&nbsp;'){
25350             html = '';
25351         }
25352         return html;
25353     },
25354
25355     /**
25356      * HTML Editor -> Textarea
25357      * Protected method that will not generally be called directly. Syncs the contents
25358      * of the editor iframe with the textarea.
25359      */
25360     syncValue : function(){
25361         if(this.initialized){
25362             var bd = (this.doc.body || this.doc.documentElement);
25363             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25364             var html = bd.innerHTML;
25365             if(Roo.isSafari){
25366                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25367                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25368                 if(m && m[1]){
25369                     html = '<div style="'+m[0]+'">' + html + '</div>';
25370                 }
25371             }
25372             html = this.cleanHtml(html);
25373             // fix up the special chars.. normaly like back quotes in word...
25374             // however we do not want to do this with chinese..
25375             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25376                 
25377                 var cc = match.charCodeAt();
25378
25379                 // Get the character value, handling surrogate pairs
25380                 if (match.length == 2) {
25381                     // It's a surrogate pair, calculate the Unicode code point
25382                     var high = match.charCodeAt(0) - 0xD800;
25383                     var low  = match.charCodeAt(1) - 0xDC00;
25384                     cc = (high * 0x400) + low + 0x10000;
25385                 }  else if (
25386                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25387                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25388                     (cc >= 0xf900 && cc < 0xfb00 )
25389                 ) {
25390                         return match;
25391                 }  
25392          
25393                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25394                 return "&#" + cc + ";";
25395                 
25396                 
25397             });
25398             
25399             
25400              
25401             if(this.owner.fireEvent('beforesync', this, html) !== false){
25402                 this.el.dom.value = html;
25403                 this.owner.fireEvent('sync', this, html);
25404             }
25405         }
25406     },
25407
25408     /**
25409      * Protected method that will not generally be called directly. Pushes the value of the textarea
25410      * into the iframe editor.
25411      */
25412     pushValue : function(){
25413         if(this.initialized){
25414             var v = this.el.dom.value.trim();
25415             
25416 //            if(v.length < 1){
25417 //                v = '&#160;';
25418 //            }
25419             
25420             if(this.owner.fireEvent('beforepush', this, v) !== false){
25421                 var d = (this.doc.body || this.doc.documentElement);
25422                 d.innerHTML = v;
25423                 this.cleanUpPaste();
25424                 this.el.dom.value = d.innerHTML;
25425                 this.owner.fireEvent('push', this, v);
25426             }
25427         }
25428     },
25429
25430     // private
25431     deferFocus : function(){
25432         this.focus.defer(10, this);
25433     },
25434
25435     // doc'ed in Field
25436     focus : function(){
25437         if(this.win && !this.sourceEditMode){
25438             this.win.focus();
25439         }else{
25440             this.el.focus();
25441         }
25442     },
25443     
25444     assignDocWin: function()
25445     {
25446         var iframe = this.iframe;
25447         
25448          if(Roo.isIE){
25449             this.doc = iframe.contentWindow.document;
25450             this.win = iframe.contentWindow;
25451         } else {
25452 //            if (!Roo.get(this.frameId)) {
25453 //                return;
25454 //            }
25455 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25456 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25457             
25458             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25459                 return;
25460             }
25461             
25462             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25463             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25464         }
25465     },
25466     
25467     // private
25468     initEditor : function(){
25469         //console.log("INIT EDITOR");
25470         this.assignDocWin();
25471         
25472         
25473         
25474         this.doc.designMode="on";
25475         this.doc.open();
25476         this.doc.write(this.getDocMarkup());
25477         this.doc.close();
25478         
25479         var dbody = (this.doc.body || this.doc.documentElement);
25480         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25481         // this copies styles from the containing element into thsi one..
25482         // not sure why we need all of this..
25483         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25484         
25485         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25486         //ss['background-attachment'] = 'fixed'; // w3c
25487         dbody.bgProperties = 'fixed'; // ie
25488         //Roo.DomHelper.applyStyles(dbody, ss);
25489         Roo.EventManager.on(this.doc, {
25490             //'mousedown': this.onEditorEvent,
25491             'mouseup': this.onEditorEvent,
25492             'dblclick': this.onEditorEvent,
25493             'click': this.onEditorEvent,
25494             'keyup': this.onEditorEvent,
25495             buffer:100,
25496             scope: this
25497         });
25498         if(Roo.isGecko){
25499             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25500         }
25501         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25502             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25503         }
25504         this.initialized = true;
25505
25506         this.owner.fireEvent('initialize', this);
25507         this.pushValue();
25508     },
25509
25510     // private
25511     onDestroy : function(){
25512         
25513         
25514         
25515         if(this.rendered){
25516             
25517             //for (var i =0; i < this.toolbars.length;i++) {
25518             //    // fixme - ask toolbars for heights?
25519             //    this.toolbars[i].onDestroy();
25520            // }
25521             
25522             //this.wrap.dom.innerHTML = '';
25523             //this.wrap.remove();
25524         }
25525     },
25526
25527     // private
25528     onFirstFocus : function(){
25529         
25530         this.assignDocWin();
25531         
25532         
25533         this.activated = true;
25534          
25535     
25536         if(Roo.isGecko){ // prevent silly gecko errors
25537             this.win.focus();
25538             var s = this.win.getSelection();
25539             if(!s.focusNode || s.focusNode.nodeType != 3){
25540                 var r = s.getRangeAt(0);
25541                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25542                 r.collapse(true);
25543                 this.deferFocus();
25544             }
25545             try{
25546                 this.execCmd('useCSS', true);
25547                 this.execCmd('styleWithCSS', false);
25548             }catch(e){}
25549         }
25550         this.owner.fireEvent('activate', this);
25551     },
25552
25553     // private
25554     adjustFont: function(btn){
25555         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25556         //if(Roo.isSafari){ // safari
25557         //    adjust *= 2;
25558        // }
25559         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25560         if(Roo.isSafari){ // safari
25561             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25562             v =  (v < 10) ? 10 : v;
25563             v =  (v > 48) ? 48 : v;
25564             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25565             
25566         }
25567         
25568         
25569         v = Math.max(1, v+adjust);
25570         
25571         this.execCmd('FontSize', v  );
25572     },
25573
25574     onEditorEvent : function(e)
25575     {
25576         this.owner.fireEvent('editorevent', this, e);
25577       //  this.updateToolbar();
25578         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25579     },
25580
25581     insertTag : function(tg)
25582     {
25583         // could be a bit smarter... -> wrap the current selected tRoo..
25584         if (tg.toLowerCase() == 'span' ||
25585             tg.toLowerCase() == 'code' ||
25586             tg.toLowerCase() == 'sup' ||
25587             tg.toLowerCase() == 'sub' 
25588             ) {
25589             
25590             range = this.createRange(this.getSelection());
25591             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25592             wrappingNode.appendChild(range.extractContents());
25593             range.insertNode(wrappingNode);
25594
25595             return;
25596             
25597             
25598             
25599         }
25600         this.execCmd("formatblock",   tg);
25601         
25602     },
25603     
25604     insertText : function(txt)
25605     {
25606         
25607         
25608         var range = this.createRange();
25609         range.deleteContents();
25610                //alert(Sender.getAttribute('label'));
25611                
25612         range.insertNode(this.doc.createTextNode(txt));
25613     } ,
25614     
25615      
25616
25617     /**
25618      * Executes a Midas editor command on the editor document and performs necessary focus and
25619      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25620      * @param {String} cmd The Midas command
25621      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25622      */
25623     relayCmd : function(cmd, value){
25624         this.win.focus();
25625         this.execCmd(cmd, value);
25626         this.owner.fireEvent('editorevent', this);
25627         //this.updateToolbar();
25628         this.owner.deferFocus();
25629     },
25630
25631     /**
25632      * Executes a Midas editor command directly on the editor document.
25633      * For visual commands, you should use {@link #relayCmd} instead.
25634      * <b>This should only be called after the editor is initialized.</b>
25635      * @param {String} cmd The Midas command
25636      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25637      */
25638     execCmd : function(cmd, value){
25639         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25640         this.syncValue();
25641     },
25642  
25643  
25644    
25645     /**
25646      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25647      * to insert tRoo.
25648      * @param {String} text | dom node.. 
25649      */
25650     insertAtCursor : function(text)
25651     {
25652         
25653         if(!this.activated){
25654             return;
25655         }
25656         /*
25657         if(Roo.isIE){
25658             this.win.focus();
25659             var r = this.doc.selection.createRange();
25660             if(r){
25661                 r.collapse(true);
25662                 r.pasteHTML(text);
25663                 this.syncValue();
25664                 this.deferFocus();
25665             
25666             }
25667             return;
25668         }
25669         */
25670         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25671             this.win.focus();
25672             
25673             
25674             // from jquery ui (MIT licenced)
25675             var range, node;
25676             var win = this.win;
25677             
25678             if (win.getSelection && win.getSelection().getRangeAt) {
25679                 range = win.getSelection().getRangeAt(0);
25680                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25681                 range.insertNode(node);
25682             } else if (win.document.selection && win.document.selection.createRange) {
25683                 // no firefox support
25684                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25685                 win.document.selection.createRange().pasteHTML(txt);
25686             } else {
25687                 // no firefox support
25688                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25689                 this.execCmd('InsertHTML', txt);
25690             } 
25691             
25692             this.syncValue();
25693             
25694             this.deferFocus();
25695         }
25696     },
25697  // private
25698     mozKeyPress : function(e){
25699         if(e.ctrlKey){
25700             var c = e.getCharCode(), cmd;
25701           
25702             if(c > 0){
25703                 c = String.fromCharCode(c).toLowerCase();
25704                 switch(c){
25705                     case 'b':
25706                         cmd = 'bold';
25707                         break;
25708                     case 'i':
25709                         cmd = 'italic';
25710                         break;
25711                     
25712                     case 'u':
25713                         cmd = 'underline';
25714                         break;
25715                     
25716                     case 'v':
25717                         this.cleanUpPaste.defer(100, this);
25718                         return;
25719                         
25720                 }
25721                 if(cmd){
25722                     this.win.focus();
25723                     this.execCmd(cmd);
25724                     this.deferFocus();
25725                     e.preventDefault();
25726                 }
25727                 
25728             }
25729         }
25730     },
25731
25732     // private
25733     fixKeys : function(){ // load time branching for fastest keydown performance
25734         if(Roo.isIE){
25735             return function(e){
25736                 var k = e.getKey(), r;
25737                 if(k == e.TAB){
25738                     e.stopEvent();
25739                     r = this.doc.selection.createRange();
25740                     if(r){
25741                         r.collapse(true);
25742                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25743                         this.deferFocus();
25744                     }
25745                     return;
25746                 }
25747                 
25748                 if(k == e.ENTER){
25749                     r = this.doc.selection.createRange();
25750                     if(r){
25751                         var target = r.parentElement();
25752                         if(!target || target.tagName.toLowerCase() != 'li'){
25753                             e.stopEvent();
25754                             r.pasteHTML('<br />');
25755                             r.collapse(false);
25756                             r.select();
25757                         }
25758                     }
25759                 }
25760                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25761                     this.cleanUpPaste.defer(100, this);
25762                     return;
25763                 }
25764                 
25765                 
25766             };
25767         }else if(Roo.isOpera){
25768             return function(e){
25769                 var k = e.getKey();
25770                 if(k == e.TAB){
25771                     e.stopEvent();
25772                     this.win.focus();
25773                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25774                     this.deferFocus();
25775                 }
25776                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25777                     this.cleanUpPaste.defer(100, this);
25778                     return;
25779                 }
25780                 
25781             };
25782         }else if(Roo.isSafari){
25783             return function(e){
25784                 var k = e.getKey();
25785                 
25786                 if(k == e.TAB){
25787                     e.stopEvent();
25788                     this.execCmd('InsertText','\t');
25789                     this.deferFocus();
25790                     return;
25791                 }
25792                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25793                     this.cleanUpPaste.defer(100, this);
25794                     return;
25795                 }
25796                 
25797              };
25798         }
25799     }(),
25800     
25801     getAllAncestors: function()
25802     {
25803         var p = this.getSelectedNode();
25804         var a = [];
25805         if (!p) {
25806             a.push(p); // push blank onto stack..
25807             p = this.getParentElement();
25808         }
25809         
25810         
25811         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25812             a.push(p);
25813             p = p.parentNode;
25814         }
25815         a.push(this.doc.body);
25816         return a;
25817     },
25818     lastSel : false,
25819     lastSelNode : false,
25820     
25821     
25822     getSelection : function() 
25823     {
25824         this.assignDocWin();
25825         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25826     },
25827     
25828     getSelectedNode: function() 
25829     {
25830         // this may only work on Gecko!!!
25831         
25832         // should we cache this!!!!
25833         
25834         
25835         
25836          
25837         var range = this.createRange(this.getSelection()).cloneRange();
25838         
25839         if (Roo.isIE) {
25840             var parent = range.parentElement();
25841             while (true) {
25842                 var testRange = range.duplicate();
25843                 testRange.moveToElementText(parent);
25844                 if (testRange.inRange(range)) {
25845                     break;
25846                 }
25847                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25848                     break;
25849                 }
25850                 parent = parent.parentElement;
25851             }
25852             return parent;
25853         }
25854         
25855         // is ancestor a text element.
25856         var ac =  range.commonAncestorContainer;
25857         if (ac.nodeType == 3) {
25858             ac = ac.parentNode;
25859         }
25860         
25861         var ar = ac.childNodes;
25862          
25863         var nodes = [];
25864         var other_nodes = [];
25865         var has_other_nodes = false;
25866         for (var i=0;i<ar.length;i++) {
25867             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25868                 continue;
25869             }
25870             // fullly contained node.
25871             
25872             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25873                 nodes.push(ar[i]);
25874                 continue;
25875             }
25876             
25877             // probably selected..
25878             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25879                 other_nodes.push(ar[i]);
25880                 continue;
25881             }
25882             // outer..
25883             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25884                 continue;
25885             }
25886             
25887             
25888             has_other_nodes = true;
25889         }
25890         if (!nodes.length && other_nodes.length) {
25891             nodes= other_nodes;
25892         }
25893         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25894             return false;
25895         }
25896         
25897         return nodes[0];
25898     },
25899     createRange: function(sel)
25900     {
25901         // this has strange effects when using with 
25902         // top toolbar - not sure if it's a great idea.
25903         //this.editor.contentWindow.focus();
25904         if (typeof sel != "undefined") {
25905             try {
25906                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25907             } catch(e) {
25908                 return this.doc.createRange();
25909             }
25910         } else {
25911             return this.doc.createRange();
25912         }
25913     },
25914     getParentElement: function()
25915     {
25916         
25917         this.assignDocWin();
25918         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25919         
25920         var range = this.createRange(sel);
25921          
25922         try {
25923             var p = range.commonAncestorContainer;
25924             while (p.nodeType == 3) { // text node
25925                 p = p.parentNode;
25926             }
25927             return p;
25928         } catch (e) {
25929             return null;
25930         }
25931     
25932     },
25933     /***
25934      *
25935      * Range intersection.. the hard stuff...
25936      *  '-1' = before
25937      *  '0' = hits..
25938      *  '1' = after.
25939      *         [ -- selected range --- ]
25940      *   [fail]                        [fail]
25941      *
25942      *    basically..
25943      *      if end is before start or  hits it. fail.
25944      *      if start is after end or hits it fail.
25945      *
25946      *   if either hits (but other is outside. - then it's not 
25947      *   
25948      *    
25949      **/
25950     
25951     
25952     // @see http://www.thismuchiknow.co.uk/?p=64.
25953     rangeIntersectsNode : function(range, node)
25954     {
25955         var nodeRange = node.ownerDocument.createRange();
25956         try {
25957             nodeRange.selectNode(node);
25958         } catch (e) {
25959             nodeRange.selectNodeContents(node);
25960         }
25961     
25962         var rangeStartRange = range.cloneRange();
25963         rangeStartRange.collapse(true);
25964     
25965         var rangeEndRange = range.cloneRange();
25966         rangeEndRange.collapse(false);
25967     
25968         var nodeStartRange = nodeRange.cloneRange();
25969         nodeStartRange.collapse(true);
25970     
25971         var nodeEndRange = nodeRange.cloneRange();
25972         nodeEndRange.collapse(false);
25973     
25974         return rangeStartRange.compareBoundaryPoints(
25975                  Range.START_TO_START, nodeEndRange) == -1 &&
25976                rangeEndRange.compareBoundaryPoints(
25977                  Range.START_TO_START, nodeStartRange) == 1;
25978         
25979          
25980     },
25981     rangeCompareNode : function(range, node)
25982     {
25983         var nodeRange = node.ownerDocument.createRange();
25984         try {
25985             nodeRange.selectNode(node);
25986         } catch (e) {
25987             nodeRange.selectNodeContents(node);
25988         }
25989         
25990         
25991         range.collapse(true);
25992     
25993         nodeRange.collapse(true);
25994      
25995         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25996         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25997          
25998         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25999         
26000         var nodeIsBefore   =  ss == 1;
26001         var nodeIsAfter    = ee == -1;
26002         
26003         if (nodeIsBefore && nodeIsAfter) {
26004             return 0; // outer
26005         }
26006         if (!nodeIsBefore && nodeIsAfter) {
26007             return 1; //right trailed.
26008         }
26009         
26010         if (nodeIsBefore && !nodeIsAfter) {
26011             return 2;  // left trailed.
26012         }
26013         // fully contined.
26014         return 3;
26015     },
26016
26017     // private? - in a new class?
26018     cleanUpPaste :  function()
26019     {
26020         // cleans up the whole document..
26021         Roo.log('cleanuppaste');
26022         
26023         this.cleanUpChildren(this.doc.body);
26024         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26025         if (clean != this.doc.body.innerHTML) {
26026             this.doc.body.innerHTML = clean;
26027         }
26028         
26029     },
26030     
26031     cleanWordChars : function(input) {// change the chars to hex code
26032         var he = Roo.HtmlEditorCore;
26033         
26034         var output = input;
26035         Roo.each(he.swapCodes, function(sw) { 
26036             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26037             
26038             output = output.replace(swapper, sw[1]);
26039         });
26040         
26041         return output;
26042     },
26043     
26044     
26045     cleanUpChildren : function (n)
26046     {
26047         if (!n.childNodes.length) {
26048             return;
26049         }
26050         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26051            this.cleanUpChild(n.childNodes[i]);
26052         }
26053     },
26054     
26055     
26056         
26057     
26058     cleanUpChild : function (node)
26059     {
26060         var ed = this;
26061         //console.log(node);
26062         if (node.nodeName == "#text") {
26063             // clean up silly Windows -- stuff?
26064             return; 
26065         }
26066         if (node.nodeName == "#comment") {
26067             node.parentNode.removeChild(node);
26068             // clean up silly Windows -- stuff?
26069             return; 
26070         }
26071         var lcname = node.tagName.toLowerCase();
26072         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26073         // whitelist of tags..
26074         
26075         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26076             // remove node.
26077             node.parentNode.removeChild(node);
26078             return;
26079             
26080         }
26081         
26082         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26083         
26084         // spans with no attributes - just remove them..
26085         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26086             remove_keep_children = true;
26087         }
26088         
26089         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26090         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26091         
26092         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26093         //    remove_keep_children = true;
26094         //}
26095         
26096         if (remove_keep_children) {
26097             this.cleanUpChildren(node);
26098             // inserts everything just before this node...
26099             while (node.childNodes.length) {
26100                 var cn = node.childNodes[0];
26101                 node.removeChild(cn);
26102                 node.parentNode.insertBefore(cn, node);
26103             }
26104             node.parentNode.removeChild(node);
26105             return;
26106         }
26107         
26108         if (!node.attributes || !node.attributes.length) {
26109             
26110           
26111             
26112             
26113             this.cleanUpChildren(node);
26114             return;
26115         }
26116         
26117         function cleanAttr(n,v)
26118         {
26119             
26120             if (v.match(/^\./) || v.match(/^\//)) {
26121                 return;
26122             }
26123             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26124                 return;
26125             }
26126             if (v.match(/^#/)) {
26127                 return;
26128             }
26129             if (v.match(/^\{/)) { // allow template editing.
26130                 return;
26131             }
26132 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26133             node.removeAttribute(n);
26134             
26135         }
26136         
26137         var cwhite = this.cwhite;
26138         var cblack = this.cblack;
26139             
26140         function cleanStyle(n,v)
26141         {
26142             if (v.match(/expression/)) { //XSS?? should we even bother..
26143                 node.removeAttribute(n);
26144                 return;
26145             }
26146             
26147             var parts = v.split(/;/);
26148             var clean = [];
26149             
26150             Roo.each(parts, function(p) {
26151                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26152                 if (!p.length) {
26153                     return true;
26154                 }
26155                 var l = p.split(':').shift().replace(/\s+/g,'');
26156                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26157                 
26158                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26159 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26160                     //node.removeAttribute(n);
26161                     return true;
26162                 }
26163                 //Roo.log()
26164                 // only allow 'c whitelisted system attributes'
26165                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26166 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26167                     //node.removeAttribute(n);
26168                     return true;
26169                 }
26170                 
26171                 
26172                  
26173                 
26174                 clean.push(p);
26175                 return true;
26176             });
26177             if (clean.length) { 
26178                 node.setAttribute(n, clean.join(';'));
26179             } else {
26180                 node.removeAttribute(n);
26181             }
26182             
26183         }
26184         
26185         
26186         for (var i = node.attributes.length-1; i > -1 ; i--) {
26187             var a = node.attributes[i];
26188             //console.log(a);
26189             
26190             if (a.name.toLowerCase().substr(0,2)=='on')  {
26191                 node.removeAttribute(a.name);
26192                 continue;
26193             }
26194             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26195                 node.removeAttribute(a.name);
26196                 continue;
26197             }
26198             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26199                 cleanAttr(a.name,a.value); // fixme..
26200                 continue;
26201             }
26202             if (a.name == 'style') {
26203                 cleanStyle(a.name,a.value);
26204                 continue;
26205             }
26206             /// clean up MS crap..
26207             // tecnically this should be a list of valid class'es..
26208             
26209             
26210             if (a.name == 'class') {
26211                 if (a.value.match(/^Mso/)) {
26212                     node.removeAttribute('class');
26213                 }
26214                 
26215                 if (a.value.match(/^body$/)) {
26216                     node.removeAttribute('class');
26217                 }
26218                 continue;
26219             }
26220             
26221             // style cleanup!?
26222             // class cleanup?
26223             
26224         }
26225         
26226         
26227         this.cleanUpChildren(node);
26228         
26229         
26230     },
26231     
26232     /**
26233      * Clean up MS wordisms...
26234      */
26235     cleanWord : function(node)
26236     {
26237         if (!node) {
26238             this.cleanWord(this.doc.body);
26239             return;
26240         }
26241         
26242         if(
26243                 node.nodeName == 'SPAN' &&
26244                 !node.hasAttributes() &&
26245                 node.childNodes.length == 1 &&
26246                 node.firstChild.nodeName == "#text"  
26247         ) {
26248             var textNode = node.firstChild;
26249             node.removeChild(textNode);
26250             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26251                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26252             }
26253             node.parentNode.insertBefore(textNode, node);
26254             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26255                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26256             }
26257             node.parentNode.removeChild(node);
26258         }
26259         
26260         if (node.nodeName == "#text") {
26261             // clean up silly Windows -- stuff?
26262             return; 
26263         }
26264         if (node.nodeName == "#comment") {
26265             node.parentNode.removeChild(node);
26266             // clean up silly Windows -- stuff?
26267             return; 
26268         }
26269         
26270         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26271             node.parentNode.removeChild(node);
26272             return;
26273         }
26274         //Roo.log(node.tagName);
26275         // remove - but keep children..
26276         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26277             //Roo.log('-- removed');
26278             while (node.childNodes.length) {
26279                 var cn = node.childNodes[0];
26280                 node.removeChild(cn);
26281                 node.parentNode.insertBefore(cn, node);
26282                 // move node to parent - and clean it..
26283                 this.cleanWord(cn);
26284             }
26285             node.parentNode.removeChild(node);
26286             /// no need to iterate chidlren = it's got none..
26287             //this.iterateChildren(node, this.cleanWord);
26288             return;
26289         }
26290         // clean styles
26291         if (node.className.length) {
26292             
26293             var cn = node.className.split(/\W+/);
26294             var cna = [];
26295             Roo.each(cn, function(cls) {
26296                 if (cls.match(/Mso[a-zA-Z]+/)) {
26297                     return;
26298                 }
26299                 cna.push(cls);
26300             });
26301             node.className = cna.length ? cna.join(' ') : '';
26302             if (!cna.length) {
26303                 node.removeAttribute("class");
26304             }
26305         }
26306         
26307         if (node.hasAttribute("lang")) {
26308             node.removeAttribute("lang");
26309         }
26310         
26311         if (node.hasAttribute("style")) {
26312             
26313             var styles = node.getAttribute("style").split(";");
26314             var nstyle = [];
26315             Roo.each(styles, function(s) {
26316                 if (!s.match(/:/)) {
26317                     return;
26318                 }
26319                 var kv = s.split(":");
26320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26321                     return;
26322                 }
26323                 // what ever is left... we allow.
26324                 nstyle.push(s);
26325             });
26326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26327             if (!nstyle.length) {
26328                 node.removeAttribute('style');
26329             }
26330         }
26331         this.iterateChildren(node, this.cleanWord);
26332         
26333         
26334         
26335     },
26336     /**
26337      * iterateChildren of a Node, calling fn each time, using this as the scole..
26338      * @param {DomNode} node node to iterate children of.
26339      * @param {Function} fn method of this class to call on each item.
26340      */
26341     iterateChildren : function(node, fn)
26342     {
26343         if (!node.childNodes.length) {
26344                 return;
26345         }
26346         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26347            fn.call(this, node.childNodes[i])
26348         }
26349     },
26350     
26351     
26352     /**
26353      * cleanTableWidths.
26354      *
26355      * Quite often pasting from word etc.. results in tables with column and widths.
26356      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26357      *
26358      */
26359     cleanTableWidths : function(node)
26360     {
26361          
26362          
26363         if (!node) {
26364             this.cleanTableWidths(this.doc.body);
26365             return;
26366         }
26367         
26368         // ignore list...
26369         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26370             return; 
26371         }
26372         Roo.log(node.tagName);
26373         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26374             this.iterateChildren(node, this.cleanTableWidths);
26375             return;
26376         }
26377         if (node.hasAttribute('width')) {
26378             node.removeAttribute('width');
26379         }
26380         
26381          
26382         if (node.hasAttribute("style")) {
26383             // pretty basic...
26384             
26385             var styles = node.getAttribute("style").split(";");
26386             var nstyle = [];
26387             Roo.each(styles, function(s) {
26388                 if (!s.match(/:/)) {
26389                     return;
26390                 }
26391                 var kv = s.split(":");
26392                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26393                     return;
26394                 }
26395                 // what ever is left... we allow.
26396                 nstyle.push(s);
26397             });
26398             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26399             if (!nstyle.length) {
26400                 node.removeAttribute('style');
26401             }
26402         }
26403         
26404         this.iterateChildren(node, this.cleanTableWidths);
26405         
26406         
26407     },
26408     
26409     
26410     
26411     
26412     domToHTML : function(currentElement, depth, nopadtext) {
26413         
26414         depth = depth || 0;
26415         nopadtext = nopadtext || false;
26416     
26417         if (!currentElement) {
26418             return this.domToHTML(this.doc.body);
26419         }
26420         
26421         //Roo.log(currentElement);
26422         var j;
26423         var allText = false;
26424         var nodeName = currentElement.nodeName;
26425         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26426         
26427         if  (nodeName == '#text') {
26428             
26429             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26430         }
26431         
26432         
26433         var ret = '';
26434         if (nodeName != 'BODY') {
26435              
26436             var i = 0;
26437             // Prints the node tagName, such as <A>, <IMG>, etc
26438             if (tagName) {
26439                 var attr = [];
26440                 for(i = 0; i < currentElement.attributes.length;i++) {
26441                     // quoting?
26442                     var aname = currentElement.attributes.item(i).name;
26443                     if (!currentElement.attributes.item(i).value.length) {
26444                         continue;
26445                     }
26446                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26447                 }
26448                 
26449                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26450             } 
26451             else {
26452                 
26453                 // eack
26454             }
26455         } else {
26456             tagName = false;
26457         }
26458         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26459             return ret;
26460         }
26461         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26462             nopadtext = true;
26463         }
26464         
26465         
26466         // Traverse the tree
26467         i = 0;
26468         var currentElementChild = currentElement.childNodes.item(i);
26469         var allText = true;
26470         var innerHTML  = '';
26471         lastnode = '';
26472         while (currentElementChild) {
26473             // Formatting code (indent the tree so it looks nice on the screen)
26474             var nopad = nopadtext;
26475             if (lastnode == 'SPAN') {
26476                 nopad  = true;
26477             }
26478             // text
26479             if  (currentElementChild.nodeName == '#text') {
26480                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26481                 toadd = nopadtext ? toadd : toadd.trim();
26482                 if (!nopad && toadd.length > 80) {
26483                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26484                 }
26485                 innerHTML  += toadd;
26486                 
26487                 i++;
26488                 currentElementChild = currentElement.childNodes.item(i);
26489                 lastNode = '';
26490                 continue;
26491             }
26492             allText = false;
26493             
26494             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26495                 
26496             // Recursively traverse the tree structure of the child node
26497             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26498             lastnode = currentElementChild.nodeName;
26499             i++;
26500             currentElementChild=currentElement.childNodes.item(i);
26501         }
26502         
26503         ret += innerHTML;
26504         
26505         if (!allText) {
26506                 // The remaining code is mostly for formatting the tree
26507             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26508         }
26509         
26510         
26511         if (tagName) {
26512             ret+= "</"+tagName+">";
26513         }
26514         return ret;
26515         
26516     },
26517         
26518     applyBlacklists : function()
26519     {
26520         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26521         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26522         
26523         this.white = [];
26524         this.black = [];
26525         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26526             if (b.indexOf(tag) > -1) {
26527                 return;
26528             }
26529             this.white.push(tag);
26530             
26531         }, this);
26532         
26533         Roo.each(w, function(tag) {
26534             if (b.indexOf(tag) > -1) {
26535                 return;
26536             }
26537             if (this.white.indexOf(tag) > -1) {
26538                 return;
26539             }
26540             this.white.push(tag);
26541             
26542         }, this);
26543         
26544         
26545         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26546             if (w.indexOf(tag) > -1) {
26547                 return;
26548             }
26549             this.black.push(tag);
26550             
26551         }, this);
26552         
26553         Roo.each(b, function(tag) {
26554             if (w.indexOf(tag) > -1) {
26555                 return;
26556             }
26557             if (this.black.indexOf(tag) > -1) {
26558                 return;
26559             }
26560             this.black.push(tag);
26561             
26562         }, this);
26563         
26564         
26565         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26566         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26567         
26568         this.cwhite = [];
26569         this.cblack = [];
26570         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26571             if (b.indexOf(tag) > -1) {
26572                 return;
26573             }
26574             this.cwhite.push(tag);
26575             
26576         }, this);
26577         
26578         Roo.each(w, function(tag) {
26579             if (b.indexOf(tag) > -1) {
26580                 return;
26581             }
26582             if (this.cwhite.indexOf(tag) > -1) {
26583                 return;
26584             }
26585             this.cwhite.push(tag);
26586             
26587         }, this);
26588         
26589         
26590         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26591             if (w.indexOf(tag) > -1) {
26592                 return;
26593             }
26594             this.cblack.push(tag);
26595             
26596         }, this);
26597         
26598         Roo.each(b, function(tag) {
26599             if (w.indexOf(tag) > -1) {
26600                 return;
26601             }
26602             if (this.cblack.indexOf(tag) > -1) {
26603                 return;
26604             }
26605             this.cblack.push(tag);
26606             
26607         }, this);
26608     },
26609     
26610     setStylesheets : function(stylesheets)
26611     {
26612         if(typeof(stylesheets) == 'string'){
26613             Roo.get(this.iframe.contentDocument.head).createChild({
26614                 tag : 'link',
26615                 rel : 'stylesheet',
26616                 type : 'text/css',
26617                 href : stylesheets
26618             });
26619             
26620             return;
26621         }
26622         var _this = this;
26623      
26624         Roo.each(stylesheets, function(s) {
26625             if(!s.length){
26626                 return;
26627             }
26628             
26629             Roo.get(_this.iframe.contentDocument.head).createChild({
26630                 tag : 'link',
26631                 rel : 'stylesheet',
26632                 type : 'text/css',
26633                 href : s
26634             });
26635         });
26636
26637         
26638     },
26639     
26640     removeStylesheets : function()
26641     {
26642         var _this = this;
26643         
26644         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26645             s.remove();
26646         });
26647     },
26648     
26649     setStyle : function(style)
26650     {
26651         Roo.get(this.iframe.contentDocument.head).createChild({
26652             tag : 'style',
26653             type : 'text/css',
26654             html : style
26655         });
26656
26657         return;
26658     }
26659     
26660     // hide stuff that is not compatible
26661     /**
26662      * @event blur
26663      * @hide
26664      */
26665     /**
26666      * @event change
26667      * @hide
26668      */
26669     /**
26670      * @event focus
26671      * @hide
26672      */
26673     /**
26674      * @event specialkey
26675      * @hide
26676      */
26677     /**
26678      * @cfg {String} fieldClass @hide
26679      */
26680     /**
26681      * @cfg {String} focusClass @hide
26682      */
26683     /**
26684      * @cfg {String} autoCreate @hide
26685      */
26686     /**
26687      * @cfg {String} inputType @hide
26688      */
26689     /**
26690      * @cfg {String} invalidClass @hide
26691      */
26692     /**
26693      * @cfg {String} invalidText @hide
26694      */
26695     /**
26696      * @cfg {String} msgFx @hide
26697      */
26698     /**
26699      * @cfg {String} validateOnBlur @hide
26700      */
26701 });
26702
26703 Roo.HtmlEditorCore.white = [
26704         'area', 'br', 'img', 'input', 'hr', 'wbr',
26705         
26706        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26707        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26708        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26709        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26710        'table',   'ul',         'xmp', 
26711        
26712        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26713       'thead',   'tr', 
26714      
26715       'dir', 'menu', 'ol', 'ul', 'dl',
26716        
26717       'embed',  'object'
26718 ];
26719
26720
26721 Roo.HtmlEditorCore.black = [
26722     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26723         'applet', // 
26724         'base',   'basefont', 'bgsound', 'blink',  'body', 
26725         'frame',  'frameset', 'head',    'html',   'ilayer', 
26726         'iframe', 'layer',  'link',     'meta',    'object',   
26727         'script', 'style' ,'title',  'xml' // clean later..
26728 ];
26729 Roo.HtmlEditorCore.clean = [
26730     'script', 'style', 'title', 'xml'
26731 ];
26732 Roo.HtmlEditorCore.remove = [
26733     'font'
26734 ];
26735 // attributes..
26736
26737 Roo.HtmlEditorCore.ablack = [
26738     'on'
26739 ];
26740     
26741 Roo.HtmlEditorCore.aclean = [ 
26742     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26743 ];
26744
26745 // protocols..
26746 Roo.HtmlEditorCore.pwhite= [
26747         'http',  'https',  'mailto'
26748 ];
26749
26750 // white listed style attributes.
26751 Roo.HtmlEditorCore.cwhite= [
26752       //  'text-align', /// default is to allow most things..
26753       
26754          
26755 //        'font-size'//??
26756 ];
26757
26758 // black listed style attributes.
26759 Roo.HtmlEditorCore.cblack= [
26760       //  'font-size' -- this can be set by the project 
26761 ];
26762
26763
26764 Roo.HtmlEditorCore.swapCodes   =[ 
26765     [    8211, "&#8211;" ], 
26766     [    8212, "&#8212;" ], 
26767     [    8216,  "'" ],  
26768     [    8217, "'" ],  
26769     [    8220, '"' ],  
26770     [    8221, '"' ],  
26771     [    8226, "*" ],  
26772     [    8230, "..." ]
26773 ]; 
26774
26775     /*
26776  * - LGPL
26777  *
26778  * HtmlEditor
26779  * 
26780  */
26781
26782 /**
26783  * @class Roo.bootstrap.HtmlEditor
26784  * @extends Roo.bootstrap.TextArea
26785  * Bootstrap HtmlEditor class
26786
26787  * @constructor
26788  * Create a new HtmlEditor
26789  * @param {Object} config The config object
26790  */
26791
26792 Roo.bootstrap.HtmlEditor = function(config){
26793     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26794     if (!this.toolbars) {
26795         this.toolbars = [];
26796     }
26797     
26798     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26799     this.addEvents({
26800             /**
26801              * @event initialize
26802              * Fires when the editor is fully initialized (including the iframe)
26803              * @param {HtmlEditor} this
26804              */
26805             initialize: true,
26806             /**
26807              * @event activate
26808              * Fires when the editor is first receives the focus. Any insertion must wait
26809              * until after this event.
26810              * @param {HtmlEditor} this
26811              */
26812             activate: true,
26813              /**
26814              * @event beforesync
26815              * Fires before the textarea is updated with content from the editor iframe. Return false
26816              * to cancel the sync.
26817              * @param {HtmlEditor} this
26818              * @param {String} html
26819              */
26820             beforesync: true,
26821              /**
26822              * @event beforepush
26823              * Fires before the iframe editor is updated with content from the textarea. Return false
26824              * to cancel the push.
26825              * @param {HtmlEditor} this
26826              * @param {String} html
26827              */
26828             beforepush: true,
26829              /**
26830              * @event sync
26831              * Fires when the textarea is updated with content from the editor iframe.
26832              * @param {HtmlEditor} this
26833              * @param {String} html
26834              */
26835             sync: true,
26836              /**
26837              * @event push
26838              * Fires when the iframe editor is updated with content from the textarea.
26839              * @param {HtmlEditor} this
26840              * @param {String} html
26841              */
26842             push: true,
26843              /**
26844              * @event editmodechange
26845              * Fires when the editor switches edit modes
26846              * @param {HtmlEditor} this
26847              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26848              */
26849             editmodechange: true,
26850             /**
26851              * @event editorevent
26852              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26853              * @param {HtmlEditor} this
26854              */
26855             editorevent: true,
26856             /**
26857              * @event firstfocus
26858              * Fires when on first focus - needed by toolbars..
26859              * @param {HtmlEditor} this
26860              */
26861             firstfocus: true,
26862             /**
26863              * @event autosave
26864              * Auto save the htmlEditor value as a file into Events
26865              * @param {HtmlEditor} this
26866              */
26867             autosave: true,
26868             /**
26869              * @event savedpreview
26870              * preview the saved version of htmlEditor
26871              * @param {HtmlEditor} this
26872              */
26873             savedpreview: true
26874         });
26875 };
26876
26877
26878 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26879     
26880     
26881       /**
26882      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26883      */
26884     toolbars : false,
26885     
26886      /**
26887     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26888     */
26889     btns : [],
26890    
26891      /**
26892      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26893      *                        Roo.resizable.
26894      */
26895     resizable : false,
26896      /**
26897      * @cfg {Number} height (in pixels)
26898      */   
26899     height: 300,
26900    /**
26901      * @cfg {Number} width (in pixels)
26902      */   
26903     width: false,
26904     
26905     /**
26906      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26907      * 
26908      */
26909     stylesheets: false,
26910     
26911     // id of frame..
26912     frameId: false,
26913     
26914     // private properties
26915     validationEvent : false,
26916     deferHeight: true,
26917     initialized : false,
26918     activated : false,
26919     
26920     onFocus : Roo.emptyFn,
26921     iframePad:3,
26922     hideMode:'offsets',
26923     
26924     tbContainer : false,
26925     
26926     bodyCls : '',
26927     
26928     toolbarContainer :function() {
26929         return this.wrap.select('.x-html-editor-tb',true).first();
26930     },
26931
26932     /**
26933      * Protected method that will not generally be called directly. It
26934      * is called when the editor creates its toolbar. Override this method if you need to
26935      * add custom toolbar buttons.
26936      * @param {HtmlEditor} editor
26937      */
26938     createToolbar : function(){
26939         Roo.log('renewing');
26940         Roo.log("create toolbars");
26941         
26942         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26943         this.toolbars[0].render(this.toolbarContainer());
26944         
26945         return;
26946         
26947 //        if (!editor.toolbars || !editor.toolbars.length) {
26948 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26949 //        }
26950 //        
26951 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26952 //            editor.toolbars[i] = Roo.factory(
26953 //                    typeof(editor.toolbars[i]) == 'string' ?
26954 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26955 //                Roo.bootstrap.HtmlEditor);
26956 //            editor.toolbars[i].init(editor);
26957 //        }
26958     },
26959
26960      
26961     // private
26962     onRender : function(ct, position)
26963     {
26964        // Roo.log("Call onRender: " + this.xtype);
26965         var _t = this;
26966         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26967       
26968         this.wrap = this.inputEl().wrap({
26969             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26970         });
26971         
26972         this.editorcore.onRender(ct, position);
26973          
26974         if (this.resizable) {
26975             this.resizeEl = new Roo.Resizable(this.wrap, {
26976                 pinned : true,
26977                 wrap: true,
26978                 dynamic : true,
26979                 minHeight : this.height,
26980                 height: this.height,
26981                 handles : this.resizable,
26982                 width: this.width,
26983                 listeners : {
26984                     resize : function(r, w, h) {
26985                         _t.onResize(w,h); // -something
26986                     }
26987                 }
26988             });
26989             
26990         }
26991         this.createToolbar(this);
26992        
26993         
26994         if(!this.width && this.resizable){
26995             this.setSize(this.wrap.getSize());
26996         }
26997         if (this.resizeEl) {
26998             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26999             // should trigger onReize..
27000         }
27001         
27002     },
27003
27004     // private
27005     onResize : function(w, h)
27006     {
27007         Roo.log('resize: ' +w + ',' + h );
27008         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27009         var ew = false;
27010         var eh = false;
27011         
27012         if(this.inputEl() ){
27013             if(typeof w == 'number'){
27014                 var aw = w - this.wrap.getFrameWidth('lr');
27015                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27016                 ew = aw;
27017             }
27018             if(typeof h == 'number'){
27019                  var tbh = -11;  // fixme it needs to tool bar size!
27020                 for (var i =0; i < this.toolbars.length;i++) {
27021                     // fixme - ask toolbars for heights?
27022                     tbh += this.toolbars[i].el.getHeight();
27023                     //if (this.toolbars[i].footer) {
27024                     //    tbh += this.toolbars[i].footer.el.getHeight();
27025                     //}
27026                 }
27027               
27028                 
27029                 
27030                 
27031                 
27032                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27033                 ah -= 5; // knock a few pixes off for look..
27034                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27035                 var eh = ah;
27036             }
27037         }
27038         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27039         this.editorcore.onResize(ew,eh);
27040         
27041     },
27042
27043     /**
27044      * Toggles the editor between standard and source edit mode.
27045      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27046      */
27047     toggleSourceEdit : function(sourceEditMode)
27048     {
27049         this.editorcore.toggleSourceEdit(sourceEditMode);
27050         
27051         if(this.editorcore.sourceEditMode){
27052             Roo.log('editor - showing textarea');
27053             
27054 //            Roo.log('in');
27055 //            Roo.log(this.syncValue());
27056             this.syncValue();
27057             this.inputEl().removeClass(['hide', 'x-hidden']);
27058             this.inputEl().dom.removeAttribute('tabIndex');
27059             this.inputEl().focus();
27060         }else{
27061             Roo.log('editor - hiding textarea');
27062 //            Roo.log('out')
27063 //            Roo.log(this.pushValue()); 
27064             this.pushValue();
27065             
27066             this.inputEl().addClass(['hide', 'x-hidden']);
27067             this.inputEl().dom.setAttribute('tabIndex', -1);
27068             //this.deferFocus();
27069         }
27070          
27071         if(this.resizable){
27072             this.setSize(this.wrap.getSize());
27073         }
27074         
27075         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27076     },
27077  
27078     // private (for BoxComponent)
27079     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27080
27081     // private (for BoxComponent)
27082     getResizeEl : function(){
27083         return this.wrap;
27084     },
27085
27086     // private (for BoxComponent)
27087     getPositionEl : function(){
27088         return this.wrap;
27089     },
27090
27091     // private
27092     initEvents : function(){
27093         this.originalValue = this.getValue();
27094     },
27095
27096 //    /**
27097 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27098 //     * @method
27099 //     */
27100 //    markInvalid : Roo.emptyFn,
27101 //    /**
27102 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27103 //     * @method
27104 //     */
27105 //    clearInvalid : Roo.emptyFn,
27106
27107     setValue : function(v){
27108         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27109         this.editorcore.pushValue();
27110     },
27111
27112      
27113     // private
27114     deferFocus : function(){
27115         this.focus.defer(10, this);
27116     },
27117
27118     // doc'ed in Field
27119     focus : function(){
27120         this.editorcore.focus();
27121         
27122     },
27123       
27124
27125     // private
27126     onDestroy : function(){
27127         
27128         
27129         
27130         if(this.rendered){
27131             
27132             for (var i =0; i < this.toolbars.length;i++) {
27133                 // fixme - ask toolbars for heights?
27134                 this.toolbars[i].onDestroy();
27135             }
27136             
27137             this.wrap.dom.innerHTML = '';
27138             this.wrap.remove();
27139         }
27140     },
27141
27142     // private
27143     onFirstFocus : function(){
27144         //Roo.log("onFirstFocus");
27145         this.editorcore.onFirstFocus();
27146          for (var i =0; i < this.toolbars.length;i++) {
27147             this.toolbars[i].onFirstFocus();
27148         }
27149         
27150     },
27151     
27152     // private
27153     syncValue : function()
27154     {   
27155         this.editorcore.syncValue();
27156     },
27157     
27158     pushValue : function()
27159     {   
27160         this.editorcore.pushValue();
27161     }
27162      
27163     
27164     // hide stuff that is not compatible
27165     /**
27166      * @event blur
27167      * @hide
27168      */
27169     /**
27170      * @event change
27171      * @hide
27172      */
27173     /**
27174      * @event focus
27175      * @hide
27176      */
27177     /**
27178      * @event specialkey
27179      * @hide
27180      */
27181     /**
27182      * @cfg {String} fieldClass @hide
27183      */
27184     /**
27185      * @cfg {String} focusClass @hide
27186      */
27187     /**
27188      * @cfg {String} autoCreate @hide
27189      */
27190     /**
27191      * @cfg {String} inputType @hide
27192      */
27193      
27194     /**
27195      * @cfg {String} invalidText @hide
27196      */
27197     /**
27198      * @cfg {String} msgFx @hide
27199      */
27200     /**
27201      * @cfg {String} validateOnBlur @hide
27202      */
27203 });
27204  
27205     
27206    
27207    
27208    
27209       
27210 Roo.namespace('Roo.bootstrap.htmleditor');
27211 /**
27212  * @class Roo.bootstrap.HtmlEditorToolbar1
27213  * Basic Toolbar
27214  * 
27215  * @example
27216  * Usage:
27217  *
27218  new Roo.bootstrap.HtmlEditor({
27219     ....
27220     toolbars : [
27221         new Roo.bootstrap.HtmlEditorToolbar1({
27222             disable : { fonts: 1 , format: 1, ..., ... , ...],
27223             btns : [ .... ]
27224         })
27225     }
27226      
27227  * 
27228  * @cfg {Object} disable List of elements to disable..
27229  * @cfg {Array} btns List of additional buttons.
27230  * 
27231  * 
27232  * NEEDS Extra CSS? 
27233  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27234  */
27235  
27236 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27237 {
27238     
27239     Roo.apply(this, config);
27240     
27241     // default disabled, based on 'good practice'..
27242     this.disable = this.disable || {};
27243     Roo.applyIf(this.disable, {
27244         fontSize : true,
27245         colors : true,
27246         specialElements : true
27247     });
27248     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27249     
27250     this.editor = config.editor;
27251     this.editorcore = config.editor.editorcore;
27252     
27253     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27254     
27255     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27256     // dont call parent... till later.
27257 }
27258 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27259      
27260     bar : true,
27261     
27262     editor : false,
27263     editorcore : false,
27264     
27265     
27266     formats : [
27267         "p" ,  
27268         "h1","h2","h3","h4","h5","h6", 
27269         "pre", "code", 
27270         "abbr", "acronym", "address", "cite", "samp", "var",
27271         'div','span'
27272     ],
27273     
27274     onRender : function(ct, position)
27275     {
27276        // Roo.log("Call onRender: " + this.xtype);
27277         
27278        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27279        Roo.log(this.el);
27280        this.el.dom.style.marginBottom = '0';
27281        var _this = this;
27282        var editorcore = this.editorcore;
27283        var editor= this.editor;
27284        
27285        var children = [];
27286        var btn = function(id,cmd , toggle, handler, html){
27287        
27288             var  event = toggle ? 'toggle' : 'click';
27289        
27290             var a = {
27291                 size : 'sm',
27292                 xtype: 'Button',
27293                 xns: Roo.bootstrap,
27294                 //glyphicon : id,
27295                 fa: id,
27296                 cmd : id || cmd,
27297                 enableToggle:toggle !== false,
27298                 html : html || '',
27299                 pressed : toggle ? false : null,
27300                 listeners : {}
27301             };
27302             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27303                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27304             };
27305             children.push(a);
27306             return a;
27307        }
27308        
27309     //    var cb_box = function...
27310         
27311         var style = {
27312                 xtype: 'Button',
27313                 size : 'sm',
27314                 xns: Roo.bootstrap,
27315                 fa : 'font',
27316                 //html : 'submit'
27317                 menu : {
27318                     xtype: 'Menu',
27319                     xns: Roo.bootstrap,
27320                     items:  []
27321                 }
27322         };
27323         Roo.each(this.formats, function(f) {
27324             style.menu.items.push({
27325                 xtype :'MenuItem',
27326                 xns: Roo.bootstrap,
27327                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27328                 tagname : f,
27329                 listeners : {
27330                     click : function()
27331                     {
27332                         editorcore.insertTag(this.tagname);
27333                         editor.focus();
27334                     }
27335                 }
27336                 
27337             });
27338         });
27339         children.push(style);   
27340         
27341         btn('bold',false,true);
27342         btn('italic',false,true);
27343         btn('align-left', 'justifyleft',true);
27344         btn('align-center', 'justifycenter',true);
27345         btn('align-right' , 'justifyright',true);
27346         btn('link', false, false, function(btn) {
27347             //Roo.log("create link?");
27348             var url = prompt(this.createLinkText, this.defaultLinkValue);
27349             if(url && url != 'http:/'+'/'){
27350                 this.editorcore.relayCmd('createlink', url);
27351             }
27352         }),
27353         btn('list','insertunorderedlist',true);
27354         btn('pencil', false,true, function(btn){
27355                 Roo.log(this);
27356                 this.toggleSourceEdit(btn.pressed);
27357         });
27358         
27359         if (this.editor.btns.length > 0) {
27360             for (var i = 0; i<this.editor.btns.length; i++) {
27361                 children.push(this.editor.btns[i]);
27362             }
27363         }
27364         
27365         /*
27366         var cog = {
27367                 xtype: 'Button',
27368                 size : 'sm',
27369                 xns: Roo.bootstrap,
27370                 glyphicon : 'cog',
27371                 //html : 'submit'
27372                 menu : {
27373                     xtype: 'Menu',
27374                     xns: Roo.bootstrap,
27375                     items:  []
27376                 }
27377         };
27378         
27379         cog.menu.items.push({
27380             xtype :'MenuItem',
27381             xns: Roo.bootstrap,
27382             html : Clean styles,
27383             tagname : f,
27384             listeners : {
27385                 click : function()
27386                 {
27387                     editorcore.insertTag(this.tagname);
27388                     editor.focus();
27389                 }
27390             }
27391             
27392         });
27393        */
27394         
27395          
27396        this.xtype = 'NavSimplebar';
27397         
27398         for(var i=0;i< children.length;i++) {
27399             
27400             this.buttons.add(this.addxtypeChild(children[i]));
27401             
27402         }
27403         
27404         editor.on('editorevent', this.updateToolbar, this);
27405     },
27406     onBtnClick : function(id)
27407     {
27408        this.editorcore.relayCmd(id);
27409        this.editorcore.focus();
27410     },
27411     
27412     /**
27413      * Protected method that will not generally be called directly. It triggers
27414      * a toolbar update by reading the markup state of the current selection in the editor.
27415      */
27416     updateToolbar: function(){
27417
27418         if(!this.editorcore.activated){
27419             this.editor.onFirstFocus(); // is this neeed?
27420             return;
27421         }
27422
27423         var btns = this.buttons; 
27424         var doc = this.editorcore.doc;
27425         btns.get('bold').setActive(doc.queryCommandState('bold'));
27426         btns.get('italic').setActive(doc.queryCommandState('italic'));
27427         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27428         
27429         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27430         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27431         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27432         
27433         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27434         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27435          /*
27436         
27437         var ans = this.editorcore.getAllAncestors();
27438         if (this.formatCombo) {
27439             
27440             
27441             var store = this.formatCombo.store;
27442             this.formatCombo.setValue("");
27443             for (var i =0; i < ans.length;i++) {
27444                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27445                     // select it..
27446                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27447                     break;
27448                 }
27449             }
27450         }
27451         
27452         
27453         
27454         // hides menus... - so this cant be on a menu...
27455         Roo.bootstrap.MenuMgr.hideAll();
27456         */
27457         Roo.bootstrap.MenuMgr.hideAll();
27458         //this.editorsyncValue();
27459     },
27460     onFirstFocus: function() {
27461         this.buttons.each(function(item){
27462            item.enable();
27463         });
27464     },
27465     toggleSourceEdit : function(sourceEditMode){
27466         
27467           
27468         if(sourceEditMode){
27469             Roo.log("disabling buttons");
27470            this.buttons.each( function(item){
27471                 if(item.cmd != 'pencil'){
27472                     item.disable();
27473                 }
27474             });
27475           
27476         }else{
27477             Roo.log("enabling buttons");
27478             if(this.editorcore.initialized){
27479                 this.buttons.each( function(item){
27480                     item.enable();
27481                 });
27482             }
27483             
27484         }
27485         Roo.log("calling toggole on editor");
27486         // tell the editor that it's been pressed..
27487         this.editor.toggleSourceEdit(sourceEditMode);
27488        
27489     }
27490 });
27491
27492
27493
27494
27495  
27496 /*
27497  * - LGPL
27498  */
27499
27500 /**
27501  * @class Roo.bootstrap.Markdown
27502  * @extends Roo.bootstrap.TextArea
27503  * Bootstrap Showdown editable area
27504  * @cfg {string} content
27505  * 
27506  * @constructor
27507  * Create a new Showdown
27508  */
27509
27510 Roo.bootstrap.Markdown = function(config){
27511     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27512    
27513 };
27514
27515 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27516     
27517     editing :false,
27518     
27519     initEvents : function()
27520     {
27521         
27522         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27523         this.markdownEl = this.el.createChild({
27524             cls : 'roo-markdown-area'
27525         });
27526         this.inputEl().addClass('d-none');
27527         if (this.getValue() == '') {
27528             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27529             
27530         } else {
27531             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27532         }
27533         this.markdownEl.on('click', this.toggleTextEdit, this);
27534         this.on('blur', this.toggleTextEdit, this);
27535         this.on('specialkey', this.resizeTextArea, this);
27536     },
27537     
27538     toggleTextEdit : function()
27539     {
27540         var sh = this.markdownEl.getHeight();
27541         this.inputEl().addClass('d-none');
27542         this.markdownEl.addClass('d-none');
27543         if (!this.editing) {
27544             // show editor?
27545             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27546             this.inputEl().removeClass('d-none');
27547             this.inputEl().focus();
27548             this.editing = true;
27549             return;
27550         }
27551         // show showdown...
27552         this.updateMarkdown();
27553         this.markdownEl.removeClass('d-none');
27554         this.editing = false;
27555         return;
27556     },
27557     updateMarkdown : function()
27558     {
27559         if (this.getValue() == '') {
27560             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27561             return;
27562         }
27563  
27564         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27565     },
27566     
27567     resizeTextArea: function () {
27568         
27569         var sh = 100;
27570         Roo.log([sh, this.getValue().split("\n").length * 30]);
27571         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27572     },
27573     setValue : function(val)
27574     {
27575         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27576         if (!this.editing) {
27577             this.updateMarkdown();
27578         }
27579         
27580     },
27581     focus : function()
27582     {
27583         if (!this.editing) {
27584             this.toggleTextEdit();
27585         }
27586         
27587     }
27588
27589
27590 });/*
27591  * Based on:
27592  * Ext JS Library 1.1.1
27593  * Copyright(c) 2006-2007, Ext JS, LLC.
27594  *
27595  * Originally Released Under LGPL - original licence link has changed is not relivant.
27596  *
27597  * Fork - LGPL
27598  * <script type="text/javascript">
27599  */
27600  
27601 /**
27602  * @class Roo.bootstrap.PagingToolbar
27603  * @extends Roo.bootstrap.NavSimplebar
27604  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27605  * @constructor
27606  * Create a new PagingToolbar
27607  * @param {Object} config The config object
27608  * @param {Roo.data.Store} store
27609  */
27610 Roo.bootstrap.PagingToolbar = function(config)
27611 {
27612     // old args format still supported... - xtype is prefered..
27613         // created from xtype...
27614     
27615     this.ds = config.dataSource;
27616     
27617     if (config.store && !this.ds) {
27618         this.store= Roo.factory(config.store, Roo.data);
27619         this.ds = this.store;
27620         this.ds.xmodule = this.xmodule || false;
27621     }
27622     
27623     this.toolbarItems = [];
27624     if (config.items) {
27625         this.toolbarItems = config.items;
27626     }
27627     
27628     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27629     
27630     this.cursor = 0;
27631     
27632     if (this.ds) { 
27633         this.bind(this.ds);
27634     }
27635     
27636     if (Roo.bootstrap.version == 4) {
27637         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27638     } else {
27639         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27640     }
27641     
27642 };
27643
27644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27645     /**
27646      * @cfg {Roo.data.Store} dataSource
27647      * The underlying data store providing the paged data
27648      */
27649     /**
27650      * @cfg {String/HTMLElement/Element} container
27651      * container The id or element that will contain the toolbar
27652      */
27653     /**
27654      * @cfg {Boolean} displayInfo
27655      * True to display the displayMsg (defaults to false)
27656      */
27657     /**
27658      * @cfg {Number} pageSize
27659      * The number of records to display per page (defaults to 20)
27660      */
27661     pageSize: 20,
27662     /**
27663      * @cfg {String} displayMsg
27664      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27665      */
27666     displayMsg : 'Displaying {0} - {1} of {2}',
27667     /**
27668      * @cfg {String} emptyMsg
27669      * The message to display when no records are found (defaults to "No data to display")
27670      */
27671     emptyMsg : 'No data to display',
27672     /**
27673      * Customizable piece of the default paging text (defaults to "Page")
27674      * @type String
27675      */
27676     beforePageText : "Page",
27677     /**
27678      * Customizable piece of the default paging text (defaults to "of %0")
27679      * @type String
27680      */
27681     afterPageText : "of {0}",
27682     /**
27683      * Customizable piece of the default paging text (defaults to "First Page")
27684      * @type String
27685      */
27686     firstText : "First Page",
27687     /**
27688      * Customizable piece of the default paging text (defaults to "Previous Page")
27689      * @type String
27690      */
27691     prevText : "Previous Page",
27692     /**
27693      * Customizable piece of the default paging text (defaults to "Next Page")
27694      * @type String
27695      */
27696     nextText : "Next Page",
27697     /**
27698      * Customizable piece of the default paging text (defaults to "Last Page")
27699      * @type String
27700      */
27701     lastText : "Last Page",
27702     /**
27703      * Customizable piece of the default paging text (defaults to "Refresh")
27704      * @type String
27705      */
27706     refreshText : "Refresh",
27707
27708     buttons : false,
27709     // private
27710     onRender : function(ct, position) 
27711     {
27712         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27713         this.navgroup.parentId = this.id;
27714         this.navgroup.onRender(this.el, null);
27715         // add the buttons to the navgroup
27716         
27717         if(this.displayInfo){
27718             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27719             this.displayEl = this.el.select('.x-paging-info', true).first();
27720 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27721 //            this.displayEl = navel.el.select('span',true).first();
27722         }
27723         
27724         var _this = this;
27725         
27726         if(this.buttons){
27727             Roo.each(_this.buttons, function(e){ // this might need to use render????
27728                Roo.factory(e).render(_this.el);
27729             });
27730         }
27731             
27732         Roo.each(_this.toolbarItems, function(e) {
27733             _this.navgroup.addItem(e);
27734         });
27735         
27736         
27737         this.first = this.navgroup.addItem({
27738             tooltip: this.firstText,
27739             cls: "prev btn-outline-secondary",
27740             html : ' <i class="fa fa-step-backward"></i>',
27741             disabled: true,
27742             preventDefault: true,
27743             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27744         });
27745         
27746         this.prev =  this.navgroup.addItem({
27747             tooltip: this.prevText,
27748             cls: "prev btn-outline-secondary",
27749             html : ' <i class="fa fa-backward"></i>',
27750             disabled: true,
27751             preventDefault: true,
27752             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27753         });
27754     //this.addSeparator();
27755         
27756         
27757         var field = this.navgroup.addItem( {
27758             tagtype : 'span',
27759             cls : 'x-paging-position  btn-outline-secondary',
27760              disabled: true,
27761             html : this.beforePageText  +
27762                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27763                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27764          } ); //?? escaped?
27765         
27766         this.field = field.el.select('input', true).first();
27767         this.field.on("keydown", this.onPagingKeydown, this);
27768         this.field.on("focus", function(){this.dom.select();});
27769     
27770     
27771         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27772         //this.field.setHeight(18);
27773         //this.addSeparator();
27774         this.next = this.navgroup.addItem({
27775             tooltip: this.nextText,
27776             cls: "next btn-outline-secondary",
27777             html : ' <i class="fa fa-forward"></i>',
27778             disabled: true,
27779             preventDefault: true,
27780             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27781         });
27782         this.last = this.navgroup.addItem({
27783             tooltip: this.lastText,
27784             html : ' <i class="fa fa-step-forward"></i>',
27785             cls: "next btn-outline-secondary",
27786             disabled: true,
27787             preventDefault: true,
27788             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27789         });
27790     //this.addSeparator();
27791         this.loading = this.navgroup.addItem({
27792             tooltip: this.refreshText,
27793             cls: "btn-outline-secondary",
27794             html : ' <i class="fa fa-refresh"></i>',
27795             preventDefault: true,
27796             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27797         });
27798         
27799     },
27800
27801     // private
27802     updateInfo : function(){
27803         if(this.displayEl){
27804             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27805             var msg = count == 0 ?
27806                 this.emptyMsg :
27807                 String.format(
27808                     this.displayMsg,
27809                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27810                 );
27811             this.displayEl.update(msg);
27812         }
27813     },
27814
27815     // private
27816     onLoad : function(ds, r, o)
27817     {
27818         this.cursor = o.params && o.params.start ? o.params.start : 0;
27819         
27820         var d = this.getPageData(),
27821             ap = d.activePage,
27822             ps = d.pages;
27823         
27824         
27825         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27826         this.field.dom.value = ap;
27827         this.first.setDisabled(ap == 1);
27828         this.prev.setDisabled(ap == 1);
27829         this.next.setDisabled(ap == ps);
27830         this.last.setDisabled(ap == ps);
27831         this.loading.enable();
27832         this.updateInfo();
27833     },
27834
27835     // private
27836     getPageData : function(){
27837         var total = this.ds.getTotalCount();
27838         return {
27839             total : total,
27840             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27841             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27842         };
27843     },
27844
27845     // private
27846     onLoadError : function(){
27847         this.loading.enable();
27848     },
27849
27850     // private
27851     onPagingKeydown : function(e){
27852         var k = e.getKey();
27853         var d = this.getPageData();
27854         if(k == e.RETURN){
27855             var v = this.field.dom.value, pageNum;
27856             if(!v || isNaN(pageNum = parseInt(v, 10))){
27857                 this.field.dom.value = d.activePage;
27858                 return;
27859             }
27860             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27861             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27862             e.stopEvent();
27863         }
27864         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))
27865         {
27866           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27867           this.field.dom.value = pageNum;
27868           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27869           e.stopEvent();
27870         }
27871         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27872         {
27873           var v = this.field.dom.value, pageNum; 
27874           var increment = (e.shiftKey) ? 10 : 1;
27875           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27876                 increment *= -1;
27877           }
27878           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27879             this.field.dom.value = d.activePage;
27880             return;
27881           }
27882           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27883           {
27884             this.field.dom.value = parseInt(v, 10) + increment;
27885             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27886             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27887           }
27888           e.stopEvent();
27889         }
27890     },
27891
27892     // private
27893     beforeLoad : function(){
27894         if(this.loading){
27895             this.loading.disable();
27896         }
27897     },
27898
27899     // private
27900     onClick : function(which){
27901         
27902         var ds = this.ds;
27903         if (!ds) {
27904             return;
27905         }
27906         
27907         switch(which){
27908             case "first":
27909                 ds.load({params:{start: 0, limit: this.pageSize}});
27910             break;
27911             case "prev":
27912                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27913             break;
27914             case "next":
27915                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27916             break;
27917             case "last":
27918                 var total = ds.getTotalCount();
27919                 var extra = total % this.pageSize;
27920                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27921                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27922             break;
27923             case "refresh":
27924                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27925             break;
27926         }
27927     },
27928
27929     /**
27930      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27931      * @param {Roo.data.Store} store The data store to unbind
27932      */
27933     unbind : function(ds){
27934         ds.un("beforeload", this.beforeLoad, this);
27935         ds.un("load", this.onLoad, this);
27936         ds.un("loadexception", this.onLoadError, this);
27937         ds.un("remove", this.updateInfo, this);
27938         ds.un("add", this.updateInfo, this);
27939         this.ds = undefined;
27940     },
27941
27942     /**
27943      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27944      * @param {Roo.data.Store} store The data store to bind
27945      */
27946     bind : function(ds){
27947         ds.on("beforeload", this.beforeLoad, this);
27948         ds.on("load", this.onLoad, this);
27949         ds.on("loadexception", this.onLoadError, this);
27950         ds.on("remove", this.updateInfo, this);
27951         ds.on("add", this.updateInfo, this);
27952         this.ds = ds;
27953     }
27954 });/*
27955  * - LGPL
27956  *
27957  * element
27958  * 
27959  */
27960
27961 /**
27962  * @class Roo.bootstrap.MessageBar
27963  * @extends Roo.bootstrap.Component
27964  * Bootstrap MessageBar class
27965  * @cfg {String} html contents of the MessageBar
27966  * @cfg {String} weight (info | success | warning | danger) default info
27967  * @cfg {String} beforeClass insert the bar before the given class
27968  * @cfg {Boolean} closable (true | false) default false
27969  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27970  * 
27971  * @constructor
27972  * Create a new Element
27973  * @param {Object} config The config object
27974  */
27975
27976 Roo.bootstrap.MessageBar = function(config){
27977     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27978 };
27979
27980 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27981     
27982     html: '',
27983     weight: 'info',
27984     closable: false,
27985     fixed: false,
27986     beforeClass: 'bootstrap-sticky-wrap',
27987     
27988     getAutoCreate : function(){
27989         
27990         var cfg = {
27991             tag: 'div',
27992             cls: 'alert alert-dismissable alert-' + this.weight,
27993             cn: [
27994                 {
27995                     tag: 'span',
27996                     cls: 'message',
27997                     html: this.html || ''
27998                 }
27999             ]
28000         };
28001         
28002         if(this.fixed){
28003             cfg.cls += ' alert-messages-fixed';
28004         }
28005         
28006         if(this.closable){
28007             cfg.cn.push({
28008                 tag: 'button',
28009                 cls: 'close',
28010                 html: 'x'
28011             });
28012         }
28013         
28014         return cfg;
28015     },
28016     
28017     onRender : function(ct, position)
28018     {
28019         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28020         
28021         if(!this.el){
28022             var cfg = Roo.apply({},  this.getAutoCreate());
28023             cfg.id = Roo.id();
28024             
28025             if (this.cls) {
28026                 cfg.cls += ' ' + this.cls;
28027             }
28028             if (this.style) {
28029                 cfg.style = this.style;
28030             }
28031             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28032             
28033             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28034         }
28035         
28036         this.el.select('>button.close').on('click', this.hide, this);
28037         
28038     },
28039     
28040     show : function()
28041     {
28042         if (!this.rendered) {
28043             this.render();
28044         }
28045         
28046         this.el.show();
28047         
28048         this.fireEvent('show', this);
28049         
28050     },
28051     
28052     hide : function()
28053     {
28054         if (!this.rendered) {
28055             this.render();
28056         }
28057         
28058         this.el.hide();
28059         
28060         this.fireEvent('hide', this);
28061     },
28062     
28063     update : function()
28064     {
28065 //        var e = this.el.dom.firstChild;
28066 //        
28067 //        if(this.closable){
28068 //            e = e.nextSibling;
28069 //        }
28070 //        
28071 //        e.data = this.html || '';
28072
28073         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28074     }
28075    
28076 });
28077
28078  
28079
28080      /*
28081  * - LGPL
28082  *
28083  * Graph
28084  * 
28085  */
28086
28087
28088 /**
28089  * @class Roo.bootstrap.Graph
28090  * @extends Roo.bootstrap.Component
28091  * Bootstrap Graph class
28092 > Prameters
28093  -sm {number} sm 4
28094  -md {number} md 5
28095  @cfg {String} graphtype  bar | vbar | pie
28096  @cfg {number} g_x coodinator | centre x (pie)
28097  @cfg {number} g_y coodinator | centre y (pie)
28098  @cfg {number} g_r radius (pie)
28099  @cfg {number} g_height height of the chart (respected by all elements in the set)
28100  @cfg {number} g_width width of the chart (respected by all elements in the set)
28101  @cfg {Object} title The title of the chart
28102     
28103  -{Array}  values
28104  -opts (object) options for the chart 
28105      o {
28106      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28107      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28108      o vgutter (number)
28109      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.
28110      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28111      o to
28112      o stretch (boolean)
28113      o }
28114  -opts (object) options for the pie
28115      o{
28116      o cut
28117      o startAngle (number)
28118      o endAngle (number)
28119      } 
28120  *
28121  * @constructor
28122  * Create a new Input
28123  * @param {Object} config The config object
28124  */
28125
28126 Roo.bootstrap.Graph = function(config){
28127     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28128     
28129     this.addEvents({
28130         // img events
28131         /**
28132          * @event click
28133          * The img click event for the img.
28134          * @param {Roo.EventObject} e
28135          */
28136         "click" : true
28137     });
28138 };
28139
28140 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28141     
28142     sm: 4,
28143     md: 5,
28144     graphtype: 'bar',
28145     g_height: 250,
28146     g_width: 400,
28147     g_x: 50,
28148     g_y: 50,
28149     g_r: 30,
28150     opts:{
28151         //g_colors: this.colors,
28152         g_type: 'soft',
28153         g_gutter: '20%'
28154
28155     },
28156     title : false,
28157
28158     getAutoCreate : function(){
28159         
28160         var cfg = {
28161             tag: 'div',
28162             html : null
28163         };
28164         
28165         
28166         return  cfg;
28167     },
28168
28169     onRender : function(ct,position){
28170         
28171         
28172         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28173         
28174         if (typeof(Raphael) == 'undefined') {
28175             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28176             return;
28177         }
28178         
28179         this.raphael = Raphael(this.el.dom);
28180         
28181                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28182                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28183                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28184                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28185                 /*
28186                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28187                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28188                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28189                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28190                 
28191                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28192                 r.barchart(330, 10, 300, 220, data1);
28193                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28194                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28195                 */
28196                 
28197                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28198                 // r.barchart(30, 30, 560, 250,  xdata, {
28199                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28200                 //     axis : "0 0 1 1",
28201                 //     axisxlabels :  xdata
28202                 //     //yvalues : cols,
28203                    
28204                 // });
28205 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28206 //        
28207 //        this.load(null,xdata,{
28208 //                axis : "0 0 1 1",
28209 //                axisxlabels :  xdata
28210 //                });
28211
28212     },
28213
28214     load : function(graphtype,xdata,opts)
28215     {
28216         this.raphael.clear();
28217         if(!graphtype) {
28218             graphtype = this.graphtype;
28219         }
28220         if(!opts){
28221             opts = this.opts;
28222         }
28223         var r = this.raphael,
28224             fin = function () {
28225                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28226             },
28227             fout = function () {
28228                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28229             },
28230             pfin = function() {
28231                 this.sector.stop();
28232                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28233
28234                 if (this.label) {
28235                     this.label[0].stop();
28236                     this.label[0].attr({ r: 7.5 });
28237                     this.label[1].attr({ "font-weight": 800 });
28238                 }
28239             },
28240             pfout = function() {
28241                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28242
28243                 if (this.label) {
28244                     this.label[0].animate({ r: 5 }, 500, "bounce");
28245                     this.label[1].attr({ "font-weight": 400 });
28246                 }
28247             };
28248
28249         switch(graphtype){
28250             case 'bar':
28251                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28252                 break;
28253             case 'hbar':
28254                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28255                 break;
28256             case 'pie':
28257 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28258 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28259 //            
28260                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28261                 
28262                 break;
28263
28264         }
28265         
28266         if(this.title){
28267             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28268         }
28269         
28270     },
28271     
28272     setTitle: function(o)
28273     {
28274         this.title = o;
28275     },
28276     
28277     initEvents: function() {
28278         
28279         if(!this.href){
28280             this.el.on('click', this.onClick, this);
28281         }
28282     },
28283     
28284     onClick : function(e)
28285     {
28286         Roo.log('img onclick');
28287         this.fireEvent('click', this, e);
28288     }
28289    
28290 });
28291
28292  
28293 /*
28294  * - LGPL
28295  *
28296  * numberBox
28297  * 
28298  */
28299 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28300
28301 /**
28302  * @class Roo.bootstrap.dash.NumberBox
28303  * @extends Roo.bootstrap.Component
28304  * Bootstrap NumberBox class
28305  * @cfg {String} headline Box headline
28306  * @cfg {String} content Box content
28307  * @cfg {String} icon Box icon
28308  * @cfg {String} footer Footer text
28309  * @cfg {String} fhref Footer href
28310  * 
28311  * @constructor
28312  * Create a new NumberBox
28313  * @param {Object} config The config object
28314  */
28315
28316
28317 Roo.bootstrap.dash.NumberBox = function(config){
28318     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28319     
28320 };
28321
28322 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28323     
28324     headline : '',
28325     content : '',
28326     icon : '',
28327     footer : '',
28328     fhref : '',
28329     ficon : '',
28330     
28331     getAutoCreate : function(){
28332         
28333         var cfg = {
28334             tag : 'div',
28335             cls : 'small-box ',
28336             cn : [
28337                 {
28338                     tag : 'div',
28339                     cls : 'inner',
28340                     cn :[
28341                         {
28342                             tag : 'h3',
28343                             cls : 'roo-headline',
28344                             html : this.headline
28345                         },
28346                         {
28347                             tag : 'p',
28348                             cls : 'roo-content',
28349                             html : this.content
28350                         }
28351                     ]
28352                 }
28353             ]
28354         };
28355         
28356         if(this.icon){
28357             cfg.cn.push({
28358                 tag : 'div',
28359                 cls : 'icon',
28360                 cn :[
28361                     {
28362                         tag : 'i',
28363                         cls : 'ion ' + this.icon
28364                     }
28365                 ]
28366             });
28367         }
28368         
28369         if(this.footer){
28370             var footer = {
28371                 tag : 'a',
28372                 cls : 'small-box-footer',
28373                 href : this.fhref || '#',
28374                 html : this.footer
28375             };
28376             
28377             cfg.cn.push(footer);
28378             
28379         }
28380         
28381         return  cfg;
28382     },
28383
28384     onRender : function(ct,position){
28385         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28386
28387
28388        
28389                 
28390     },
28391
28392     setHeadline: function (value)
28393     {
28394         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28395     },
28396     
28397     setFooter: function (value, href)
28398     {
28399         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28400         
28401         if(href){
28402             this.el.select('a.small-box-footer',true).first().attr('href', href);
28403         }
28404         
28405     },
28406
28407     setContent: function (value)
28408     {
28409         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28410     },
28411
28412     initEvents: function() 
28413     {   
28414         
28415     }
28416     
28417 });
28418
28419  
28420 /*
28421  * - LGPL
28422  *
28423  * TabBox
28424  * 
28425  */
28426 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28427
28428 /**
28429  * @class Roo.bootstrap.dash.TabBox
28430  * @extends Roo.bootstrap.Component
28431  * Bootstrap TabBox class
28432  * @cfg {String} title Title of the TabBox
28433  * @cfg {String} icon Icon of the TabBox
28434  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28435  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28436  * 
28437  * @constructor
28438  * Create a new TabBox
28439  * @param {Object} config The config object
28440  */
28441
28442
28443 Roo.bootstrap.dash.TabBox = function(config){
28444     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28445     this.addEvents({
28446         // raw events
28447         /**
28448          * @event addpane
28449          * When a pane is added
28450          * @param {Roo.bootstrap.dash.TabPane} pane
28451          */
28452         "addpane" : true,
28453         /**
28454          * @event activatepane
28455          * When a pane is activated
28456          * @param {Roo.bootstrap.dash.TabPane} pane
28457          */
28458         "activatepane" : true
28459         
28460          
28461     });
28462     
28463     this.panes = [];
28464 };
28465
28466 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28467
28468     title : '',
28469     icon : false,
28470     showtabs : true,
28471     tabScrollable : false,
28472     
28473     getChildContainer : function()
28474     {
28475         return this.el.select('.tab-content', true).first();
28476     },
28477     
28478     getAutoCreate : function(){
28479         
28480         var header = {
28481             tag: 'li',
28482             cls: 'pull-left header',
28483             html: this.title,
28484             cn : []
28485         };
28486         
28487         if(this.icon){
28488             header.cn.push({
28489                 tag: 'i',
28490                 cls: 'fa ' + this.icon
28491             });
28492         }
28493         
28494         var h = {
28495             tag: 'ul',
28496             cls: 'nav nav-tabs pull-right',
28497             cn: [
28498                 header
28499             ]
28500         };
28501         
28502         if(this.tabScrollable){
28503             h = {
28504                 tag: 'div',
28505                 cls: 'tab-header',
28506                 cn: [
28507                     {
28508                         tag: 'ul',
28509                         cls: 'nav nav-tabs pull-right',
28510                         cn: [
28511                             header
28512                         ]
28513                     }
28514                 ]
28515             };
28516         }
28517         
28518         var cfg = {
28519             tag: 'div',
28520             cls: 'nav-tabs-custom',
28521             cn: [
28522                 h,
28523                 {
28524                     tag: 'div',
28525                     cls: 'tab-content no-padding',
28526                     cn: []
28527                 }
28528             ]
28529         };
28530
28531         return  cfg;
28532     },
28533     initEvents : function()
28534     {
28535         //Roo.log('add add pane handler');
28536         this.on('addpane', this.onAddPane, this);
28537     },
28538      /**
28539      * Updates the box title
28540      * @param {String} html to set the title to.
28541      */
28542     setTitle : function(value)
28543     {
28544         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28545     },
28546     onAddPane : function(pane)
28547     {
28548         this.panes.push(pane);
28549         //Roo.log('addpane');
28550         //Roo.log(pane);
28551         // tabs are rendere left to right..
28552         if(!this.showtabs){
28553             return;
28554         }
28555         
28556         var ctr = this.el.select('.nav-tabs', true).first();
28557          
28558          
28559         var existing = ctr.select('.nav-tab',true);
28560         var qty = existing.getCount();;
28561         
28562         
28563         var tab = ctr.createChild({
28564             tag : 'li',
28565             cls : 'nav-tab' + (qty ? '' : ' active'),
28566             cn : [
28567                 {
28568                     tag : 'a',
28569                     href:'#',
28570                     html : pane.title
28571                 }
28572             ]
28573         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28574         pane.tab = tab;
28575         
28576         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28577         if (!qty) {
28578             pane.el.addClass('active');
28579         }
28580         
28581                 
28582     },
28583     onTabClick : function(ev,un,ob,pane)
28584     {
28585         //Roo.log('tab - prev default');
28586         ev.preventDefault();
28587         
28588         
28589         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28590         pane.tab.addClass('active');
28591         //Roo.log(pane.title);
28592         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28593         // technically we should have a deactivate event.. but maybe add later.
28594         // and it should not de-activate the selected tab...
28595         this.fireEvent('activatepane', pane);
28596         pane.el.addClass('active');
28597         pane.fireEvent('activate');
28598         
28599         
28600     },
28601     
28602     getActivePane : function()
28603     {
28604         var r = false;
28605         Roo.each(this.panes, function(p) {
28606             if(p.el.hasClass('active')){
28607                 r = p;
28608                 return false;
28609             }
28610             
28611             return;
28612         });
28613         
28614         return r;
28615     }
28616     
28617     
28618 });
28619
28620  
28621 /*
28622  * - LGPL
28623  *
28624  * Tab pane
28625  * 
28626  */
28627 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28628 /**
28629  * @class Roo.bootstrap.TabPane
28630  * @extends Roo.bootstrap.Component
28631  * Bootstrap TabPane class
28632  * @cfg {Boolean} active (false | true) Default false
28633  * @cfg {String} title title of panel
28634
28635  * 
28636  * @constructor
28637  * Create a new TabPane
28638  * @param {Object} config The config object
28639  */
28640
28641 Roo.bootstrap.dash.TabPane = function(config){
28642     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28643     
28644     this.addEvents({
28645         // raw events
28646         /**
28647          * @event activate
28648          * When a pane is activated
28649          * @param {Roo.bootstrap.dash.TabPane} pane
28650          */
28651         "activate" : true
28652          
28653     });
28654 };
28655
28656 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28657     
28658     active : false,
28659     title : '',
28660     
28661     // the tabBox that this is attached to.
28662     tab : false,
28663      
28664     getAutoCreate : function() 
28665     {
28666         var cfg = {
28667             tag: 'div',
28668             cls: 'tab-pane'
28669         };
28670         
28671         if(this.active){
28672             cfg.cls += ' active';
28673         }
28674         
28675         return cfg;
28676     },
28677     initEvents  : function()
28678     {
28679         //Roo.log('trigger add pane handler');
28680         this.parent().fireEvent('addpane', this)
28681     },
28682     
28683      /**
28684      * Updates the tab title 
28685      * @param {String} html to set the title to.
28686      */
28687     setTitle: function(str)
28688     {
28689         if (!this.tab) {
28690             return;
28691         }
28692         this.title = str;
28693         this.tab.select('a', true).first().dom.innerHTML = str;
28694         
28695     }
28696     
28697     
28698     
28699 });
28700
28701  
28702
28703
28704  /*
28705  * - LGPL
28706  *
28707  * menu
28708  * 
28709  */
28710 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28711
28712 /**
28713  * @class Roo.bootstrap.menu.Menu
28714  * @extends Roo.bootstrap.Component
28715  * Bootstrap Menu class - container for Menu
28716  * @cfg {String} html Text of the menu
28717  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28718  * @cfg {String} icon Font awesome icon
28719  * @cfg {String} pos Menu align to (top | bottom) default bottom
28720  * 
28721  * 
28722  * @constructor
28723  * Create a new Menu
28724  * @param {Object} config The config object
28725  */
28726
28727
28728 Roo.bootstrap.menu.Menu = function(config){
28729     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28730     
28731     this.addEvents({
28732         /**
28733          * @event beforeshow
28734          * Fires before this menu is displayed
28735          * @param {Roo.bootstrap.menu.Menu} this
28736          */
28737         beforeshow : true,
28738         /**
28739          * @event beforehide
28740          * Fires before this menu is hidden
28741          * @param {Roo.bootstrap.menu.Menu} this
28742          */
28743         beforehide : true,
28744         /**
28745          * @event show
28746          * Fires after this menu is displayed
28747          * @param {Roo.bootstrap.menu.Menu} this
28748          */
28749         show : true,
28750         /**
28751          * @event hide
28752          * Fires after this menu is hidden
28753          * @param {Roo.bootstrap.menu.Menu} this
28754          */
28755         hide : true,
28756         /**
28757          * @event click
28758          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28759          * @param {Roo.bootstrap.menu.Menu} this
28760          * @param {Roo.EventObject} e
28761          */
28762         click : true
28763     });
28764     
28765 };
28766
28767 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28768     
28769     submenu : false,
28770     html : '',
28771     weight : 'default',
28772     icon : false,
28773     pos : 'bottom',
28774     
28775     
28776     getChildContainer : function() {
28777         if(this.isSubMenu){
28778             return this.el;
28779         }
28780         
28781         return this.el.select('ul.dropdown-menu', true).first();  
28782     },
28783     
28784     getAutoCreate : function()
28785     {
28786         var text = [
28787             {
28788                 tag : 'span',
28789                 cls : 'roo-menu-text',
28790                 html : this.html
28791             }
28792         ];
28793         
28794         if(this.icon){
28795             text.unshift({
28796                 tag : 'i',
28797                 cls : 'fa ' + this.icon
28798             })
28799         }
28800         
28801         
28802         var cfg = {
28803             tag : 'div',
28804             cls : 'btn-group',
28805             cn : [
28806                 {
28807                     tag : 'button',
28808                     cls : 'dropdown-button btn btn-' + this.weight,
28809                     cn : text
28810                 },
28811                 {
28812                     tag : 'button',
28813                     cls : 'dropdown-toggle btn btn-' + this.weight,
28814                     cn : [
28815                         {
28816                             tag : 'span',
28817                             cls : 'caret'
28818                         }
28819                     ]
28820                 },
28821                 {
28822                     tag : 'ul',
28823                     cls : 'dropdown-menu'
28824                 }
28825             ]
28826             
28827         };
28828         
28829         if(this.pos == 'top'){
28830             cfg.cls += ' dropup';
28831         }
28832         
28833         if(this.isSubMenu){
28834             cfg = {
28835                 tag : 'ul',
28836                 cls : 'dropdown-menu'
28837             }
28838         }
28839         
28840         return cfg;
28841     },
28842     
28843     onRender : function(ct, position)
28844     {
28845         this.isSubMenu = ct.hasClass('dropdown-submenu');
28846         
28847         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28848     },
28849     
28850     initEvents : function() 
28851     {
28852         if(this.isSubMenu){
28853             return;
28854         }
28855         
28856         this.hidden = true;
28857         
28858         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28859         this.triggerEl.on('click', this.onTriggerPress, this);
28860         
28861         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28862         this.buttonEl.on('click', this.onClick, this);
28863         
28864     },
28865     
28866     list : function()
28867     {
28868         if(this.isSubMenu){
28869             return this.el;
28870         }
28871         
28872         return this.el.select('ul.dropdown-menu', true).first();
28873     },
28874     
28875     onClick : function(e)
28876     {
28877         this.fireEvent("click", this, e);
28878     },
28879     
28880     onTriggerPress  : function(e)
28881     {   
28882         if (this.isVisible()) {
28883             this.hide();
28884         } else {
28885             this.show();
28886         }
28887     },
28888     
28889     isVisible : function(){
28890         return !this.hidden;
28891     },
28892     
28893     show : function()
28894     {
28895         this.fireEvent("beforeshow", this);
28896         
28897         this.hidden = false;
28898         this.el.addClass('open');
28899         
28900         Roo.get(document).on("mouseup", this.onMouseUp, this);
28901         
28902         this.fireEvent("show", this);
28903         
28904         
28905     },
28906     
28907     hide : function()
28908     {
28909         this.fireEvent("beforehide", this);
28910         
28911         this.hidden = true;
28912         this.el.removeClass('open');
28913         
28914         Roo.get(document).un("mouseup", this.onMouseUp);
28915         
28916         this.fireEvent("hide", this);
28917     },
28918     
28919     onMouseUp : function()
28920     {
28921         this.hide();
28922     }
28923     
28924 });
28925
28926  
28927  /*
28928  * - LGPL
28929  *
28930  * menu item
28931  * 
28932  */
28933 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28934
28935 /**
28936  * @class Roo.bootstrap.menu.Item
28937  * @extends Roo.bootstrap.Component
28938  * Bootstrap MenuItem class
28939  * @cfg {Boolean} submenu (true | false) default false
28940  * @cfg {String} html text of the item
28941  * @cfg {String} href the link
28942  * @cfg {Boolean} disable (true | false) default false
28943  * @cfg {Boolean} preventDefault (true | false) default true
28944  * @cfg {String} icon Font awesome icon
28945  * @cfg {String} pos Submenu align to (left | right) default right 
28946  * 
28947  * 
28948  * @constructor
28949  * Create a new Item
28950  * @param {Object} config The config object
28951  */
28952
28953
28954 Roo.bootstrap.menu.Item = function(config){
28955     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28956     this.addEvents({
28957         /**
28958          * @event mouseover
28959          * Fires when the mouse is hovering over this menu
28960          * @param {Roo.bootstrap.menu.Item} this
28961          * @param {Roo.EventObject} e
28962          */
28963         mouseover : true,
28964         /**
28965          * @event mouseout
28966          * Fires when the mouse exits this menu
28967          * @param {Roo.bootstrap.menu.Item} this
28968          * @param {Roo.EventObject} e
28969          */
28970         mouseout : true,
28971         // raw events
28972         /**
28973          * @event click
28974          * The raw click event for the entire grid.
28975          * @param {Roo.EventObject} e
28976          */
28977         click : true
28978     });
28979 };
28980
28981 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28982     
28983     submenu : false,
28984     href : '',
28985     html : '',
28986     preventDefault: true,
28987     disable : false,
28988     icon : false,
28989     pos : 'right',
28990     
28991     getAutoCreate : function()
28992     {
28993         var text = [
28994             {
28995                 tag : 'span',
28996                 cls : 'roo-menu-item-text',
28997                 html : this.html
28998             }
28999         ];
29000         
29001         if(this.icon){
29002             text.unshift({
29003                 tag : 'i',
29004                 cls : 'fa ' + this.icon
29005             })
29006         }
29007         
29008         var cfg = {
29009             tag : 'li',
29010             cn : [
29011                 {
29012                     tag : 'a',
29013                     href : this.href || '#',
29014                     cn : text
29015                 }
29016             ]
29017         };
29018         
29019         if(this.disable){
29020             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29021         }
29022         
29023         if(this.submenu){
29024             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29025             
29026             if(this.pos == 'left'){
29027                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29028             }
29029         }
29030         
29031         return cfg;
29032     },
29033     
29034     initEvents : function() 
29035     {
29036         this.el.on('mouseover', this.onMouseOver, this);
29037         this.el.on('mouseout', this.onMouseOut, this);
29038         
29039         this.el.select('a', true).first().on('click', this.onClick, this);
29040         
29041     },
29042     
29043     onClick : function(e)
29044     {
29045         if(this.preventDefault){
29046             e.preventDefault();
29047         }
29048         
29049         this.fireEvent("click", this, e);
29050     },
29051     
29052     onMouseOver : function(e)
29053     {
29054         if(this.submenu && this.pos == 'left'){
29055             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29056         }
29057         
29058         this.fireEvent("mouseover", this, e);
29059     },
29060     
29061     onMouseOut : function(e)
29062     {
29063         this.fireEvent("mouseout", this, e);
29064     }
29065 });
29066
29067  
29068
29069  /*
29070  * - LGPL
29071  *
29072  * menu separator
29073  * 
29074  */
29075 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29076
29077 /**
29078  * @class Roo.bootstrap.menu.Separator
29079  * @extends Roo.bootstrap.Component
29080  * Bootstrap Separator class
29081  * 
29082  * @constructor
29083  * Create a new Separator
29084  * @param {Object} config The config object
29085  */
29086
29087
29088 Roo.bootstrap.menu.Separator = function(config){
29089     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29090 };
29091
29092 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29093     
29094     getAutoCreate : function(){
29095         var cfg = {
29096             tag : 'li',
29097             cls: 'dropdown-divider divider'
29098         };
29099         
29100         return cfg;
29101     }
29102    
29103 });
29104
29105  
29106
29107  /*
29108  * - LGPL
29109  *
29110  * Tooltip
29111  * 
29112  */
29113
29114 /**
29115  * @class Roo.bootstrap.Tooltip
29116  * Bootstrap Tooltip class
29117  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29118  * to determine which dom element triggers the tooltip.
29119  * 
29120  * It needs to add support for additional attributes like tooltip-position
29121  * 
29122  * @constructor
29123  * Create a new Toolti
29124  * @param {Object} config The config object
29125  */
29126
29127 Roo.bootstrap.Tooltip = function(config){
29128     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29129     
29130     this.alignment = Roo.bootstrap.Tooltip.alignment;
29131     
29132     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29133         this.alignment = config.alignment;
29134     }
29135     
29136 };
29137
29138 Roo.apply(Roo.bootstrap.Tooltip, {
29139     /**
29140      * @function init initialize tooltip monitoring.
29141      * @static
29142      */
29143     currentEl : false,
29144     currentTip : false,
29145     currentRegion : false,
29146     
29147     //  init : delay?
29148     
29149     init : function()
29150     {
29151         Roo.get(document).on('mouseover', this.enter ,this);
29152         Roo.get(document).on('mouseout', this.leave, this);
29153          
29154         
29155         this.currentTip = new Roo.bootstrap.Tooltip();
29156     },
29157     
29158     enter : function(ev)
29159     {
29160         var dom = ev.getTarget();
29161         
29162         //Roo.log(['enter',dom]);
29163         var el = Roo.fly(dom);
29164         if (this.currentEl) {
29165             //Roo.log(dom);
29166             //Roo.log(this.currentEl);
29167             //Roo.log(this.currentEl.contains(dom));
29168             if (this.currentEl == el) {
29169                 return;
29170             }
29171             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29172                 return;
29173             }
29174
29175         }
29176         
29177         if (this.currentTip.el) {
29178             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29179         }    
29180         //Roo.log(ev);
29181         
29182         if(!el || el.dom == document){
29183             return;
29184         }
29185         
29186         var bindEl = el; 
29187         var pel = false;
29188         if (!el.attr('tooltip')) {
29189             pel = el.findParent("[tooltip]");
29190             if (pel) {
29191                 bindEl = Roo.get(pel);
29192             }
29193         }
29194         
29195        
29196         
29197         // you can not look for children, as if el is the body.. then everythign is the child..
29198         if (!pel && !el.attr('tooltip')) { //
29199             if (!el.select("[tooltip]").elements.length) {
29200                 return;
29201             }
29202             // is the mouse over this child...?
29203             bindEl = el.select("[tooltip]").first();
29204             var xy = ev.getXY();
29205             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29206                 //Roo.log("not in region.");
29207                 return;
29208             }
29209             //Roo.log("child element over..");
29210             
29211         }
29212         this.currentEl = el;
29213         this.currentTip.bind(bindEl);
29214         this.currentRegion = Roo.lib.Region.getRegion(dom);
29215         this.currentTip.enter();
29216         
29217     },
29218     leave : function(ev)
29219     {
29220         var dom = ev.getTarget();
29221         //Roo.log(['leave',dom]);
29222         if (!this.currentEl) {
29223             return;
29224         }
29225         
29226         
29227         if (dom != this.currentEl.dom) {
29228             return;
29229         }
29230         var xy = ev.getXY();
29231         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29232             return;
29233         }
29234         // only activate leave if mouse cursor is outside... bounding box..
29235         
29236         
29237         
29238         
29239         if (this.currentTip) {
29240             this.currentTip.leave();
29241         }
29242         //Roo.log('clear currentEl');
29243         this.currentEl = false;
29244         
29245         
29246     },
29247     alignment : {
29248         'left' : ['r-l', [-2,0], 'right'],
29249         'right' : ['l-r', [2,0], 'left'],
29250         'bottom' : ['t-b', [0,2], 'top'],
29251         'top' : [ 'b-t', [0,-2], 'bottom']
29252     }
29253     
29254 });
29255
29256
29257 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29258     
29259     
29260     bindEl : false,
29261     
29262     delay : null, // can be { show : 300 , hide: 500}
29263     
29264     timeout : null,
29265     
29266     hoverState : null, //???
29267     
29268     placement : 'bottom', 
29269     
29270     alignment : false,
29271     
29272     getAutoCreate : function(){
29273     
29274         var cfg = {
29275            cls : 'tooltip',   
29276            role : 'tooltip',
29277            cn : [
29278                 {
29279                     cls : 'tooltip-arrow arrow'
29280                 },
29281                 {
29282                     cls : 'tooltip-inner'
29283                 }
29284            ]
29285         };
29286         
29287         return cfg;
29288     },
29289     bind : function(el)
29290     {
29291         this.bindEl = el;
29292     },
29293     
29294     initEvents : function()
29295     {
29296         this.arrowEl = this.el.select('.arrow', true).first();
29297         this.innerEl = this.el.select('.tooltip-inner', true).first();
29298     },
29299     
29300     enter : function () {
29301        
29302         if (this.timeout != null) {
29303             clearTimeout(this.timeout);
29304         }
29305         
29306         this.hoverState = 'in';
29307          //Roo.log("enter - show");
29308         if (!this.delay || !this.delay.show) {
29309             this.show();
29310             return;
29311         }
29312         var _t = this;
29313         this.timeout = setTimeout(function () {
29314             if (_t.hoverState == 'in') {
29315                 _t.show();
29316             }
29317         }, this.delay.show);
29318     },
29319     leave : function()
29320     {
29321         clearTimeout(this.timeout);
29322     
29323         this.hoverState = 'out';
29324          if (!this.delay || !this.delay.hide) {
29325             this.hide();
29326             return;
29327         }
29328        
29329         var _t = this;
29330         this.timeout = setTimeout(function () {
29331             //Roo.log("leave - timeout");
29332             
29333             if (_t.hoverState == 'out') {
29334                 _t.hide();
29335                 Roo.bootstrap.Tooltip.currentEl = false;
29336             }
29337         }, delay);
29338     },
29339     
29340     show : function (msg)
29341     {
29342         if (!this.el) {
29343             this.render(document.body);
29344         }
29345         // set content.
29346         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29347         
29348         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29349         
29350         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29351         
29352         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29353                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29354         
29355         var placement = typeof this.placement == 'function' ?
29356             this.placement.call(this, this.el, on_el) :
29357             this.placement;
29358             
29359         var autoToken = /\s?auto?\s?/i;
29360         var autoPlace = autoToken.test(placement);
29361         if (autoPlace) {
29362             placement = placement.replace(autoToken, '') || 'top';
29363         }
29364         
29365         //this.el.detach()
29366         //this.el.setXY([0,0]);
29367         this.el.show();
29368         //this.el.dom.style.display='block';
29369         
29370         //this.el.appendTo(on_el);
29371         
29372         var p = this.getPosition();
29373         var box = this.el.getBox();
29374         
29375         if (autoPlace) {
29376             // fixme..
29377         }
29378         
29379         var align = this.alignment[placement];
29380         
29381         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29382         
29383         if(placement == 'top' || placement == 'bottom'){
29384             if(xy[0] < 0){
29385                 placement = 'right';
29386             }
29387             
29388             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29389                 placement = 'left';
29390             }
29391             
29392             var scroll = Roo.select('body', true).first().getScroll();
29393             
29394             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29395                 placement = 'top';
29396             }
29397             
29398             align = this.alignment[placement];
29399             
29400             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29401             
29402         }
29403         
29404         var elems = document.getElementsByTagName('div');
29405         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29406         for (var i = 0; i < elems.length; i++) {
29407           var zindex = Number.parseInt(
29408                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29409                 10
29410           );
29411           if (zindex > highest) {
29412             highest = zindex;
29413           }
29414         }
29415         
29416         
29417         
29418         this.el.dom.style.zIndex = highest;
29419         
29420         this.el.alignTo(this.bindEl, align[0],align[1]);
29421         //var arrow = this.el.select('.arrow',true).first();
29422         //arrow.set(align[2], 
29423         
29424         this.el.addClass(placement);
29425         this.el.addClass("bs-tooltip-"+ placement);
29426         
29427         this.el.addClass('in fade show');
29428         
29429         this.hoverState = null;
29430         
29431         if (this.el.hasClass('fade')) {
29432             // fade it?
29433         }
29434         
29435         
29436         
29437         
29438         
29439     },
29440     hide : function()
29441     {
29442          
29443         if (!this.el) {
29444             return;
29445         }
29446         //this.el.setXY([0,0]);
29447         this.el.removeClass(['show', 'in']);
29448         //this.el.hide();
29449         
29450     }
29451     
29452 });
29453  
29454
29455  /*
29456  * - LGPL
29457  *
29458  * Location Picker
29459  * 
29460  */
29461
29462 /**
29463  * @class Roo.bootstrap.LocationPicker
29464  * @extends Roo.bootstrap.Component
29465  * Bootstrap LocationPicker class
29466  * @cfg {Number} latitude Position when init default 0
29467  * @cfg {Number} longitude Position when init default 0
29468  * @cfg {Number} zoom default 15
29469  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29470  * @cfg {Boolean} mapTypeControl default false
29471  * @cfg {Boolean} disableDoubleClickZoom default false
29472  * @cfg {Boolean} scrollwheel default true
29473  * @cfg {Boolean} streetViewControl default false
29474  * @cfg {Number} radius default 0
29475  * @cfg {String} locationName
29476  * @cfg {Boolean} draggable default true
29477  * @cfg {Boolean} enableAutocomplete default false
29478  * @cfg {Boolean} enableReverseGeocode default true
29479  * @cfg {String} markerTitle
29480  * 
29481  * @constructor
29482  * Create a new LocationPicker
29483  * @param {Object} config The config object
29484  */
29485
29486
29487 Roo.bootstrap.LocationPicker = function(config){
29488     
29489     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29490     
29491     this.addEvents({
29492         /**
29493          * @event initial
29494          * Fires when the picker initialized.
29495          * @param {Roo.bootstrap.LocationPicker} this
29496          * @param {Google Location} location
29497          */
29498         initial : true,
29499         /**
29500          * @event positionchanged
29501          * Fires when the picker position changed.
29502          * @param {Roo.bootstrap.LocationPicker} this
29503          * @param {Google Location} location
29504          */
29505         positionchanged : true,
29506         /**
29507          * @event resize
29508          * Fires when the map resize.
29509          * @param {Roo.bootstrap.LocationPicker} this
29510          */
29511         resize : true,
29512         /**
29513          * @event show
29514          * Fires when the map show.
29515          * @param {Roo.bootstrap.LocationPicker} this
29516          */
29517         show : true,
29518         /**
29519          * @event hide
29520          * Fires when the map hide.
29521          * @param {Roo.bootstrap.LocationPicker} this
29522          */
29523         hide : true,
29524         /**
29525          * @event mapClick
29526          * Fires when click the map.
29527          * @param {Roo.bootstrap.LocationPicker} this
29528          * @param {Map event} e
29529          */
29530         mapClick : true,
29531         /**
29532          * @event mapRightClick
29533          * Fires when right click the map.
29534          * @param {Roo.bootstrap.LocationPicker} this
29535          * @param {Map event} e
29536          */
29537         mapRightClick : true,
29538         /**
29539          * @event markerClick
29540          * Fires when click the marker.
29541          * @param {Roo.bootstrap.LocationPicker} this
29542          * @param {Map event} e
29543          */
29544         markerClick : true,
29545         /**
29546          * @event markerRightClick
29547          * Fires when right click the marker.
29548          * @param {Roo.bootstrap.LocationPicker} this
29549          * @param {Map event} e
29550          */
29551         markerRightClick : true,
29552         /**
29553          * @event OverlayViewDraw
29554          * Fires when OverlayView Draw
29555          * @param {Roo.bootstrap.LocationPicker} this
29556          */
29557         OverlayViewDraw : true,
29558         /**
29559          * @event OverlayViewOnAdd
29560          * Fires when OverlayView Draw
29561          * @param {Roo.bootstrap.LocationPicker} this
29562          */
29563         OverlayViewOnAdd : true,
29564         /**
29565          * @event OverlayViewOnRemove
29566          * Fires when OverlayView Draw
29567          * @param {Roo.bootstrap.LocationPicker} this
29568          */
29569         OverlayViewOnRemove : true,
29570         /**
29571          * @event OverlayViewShow
29572          * Fires when OverlayView Draw
29573          * @param {Roo.bootstrap.LocationPicker} this
29574          * @param {Pixel} cpx
29575          */
29576         OverlayViewShow : true,
29577         /**
29578          * @event OverlayViewHide
29579          * Fires when OverlayView Draw
29580          * @param {Roo.bootstrap.LocationPicker} this
29581          */
29582         OverlayViewHide : true,
29583         /**
29584          * @event loadexception
29585          * Fires when load google lib failed.
29586          * @param {Roo.bootstrap.LocationPicker} this
29587          */
29588         loadexception : true
29589     });
29590         
29591 };
29592
29593 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29594     
29595     gMapContext: false,
29596     
29597     latitude: 0,
29598     longitude: 0,
29599     zoom: 15,
29600     mapTypeId: false,
29601     mapTypeControl: false,
29602     disableDoubleClickZoom: false,
29603     scrollwheel: true,
29604     streetViewControl: false,
29605     radius: 0,
29606     locationName: '',
29607     draggable: true,
29608     enableAutocomplete: false,
29609     enableReverseGeocode: true,
29610     markerTitle: '',
29611     
29612     getAutoCreate: function()
29613     {
29614
29615         var cfg = {
29616             tag: 'div',
29617             cls: 'roo-location-picker'
29618         };
29619         
29620         return cfg
29621     },
29622     
29623     initEvents: function(ct, position)
29624     {       
29625         if(!this.el.getWidth() || this.isApplied()){
29626             return;
29627         }
29628         
29629         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29630         
29631         this.initial();
29632     },
29633     
29634     initial: function()
29635     {
29636         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29637             this.fireEvent('loadexception', this);
29638             return;
29639         }
29640         
29641         if(!this.mapTypeId){
29642             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29643         }
29644         
29645         this.gMapContext = this.GMapContext();
29646         
29647         this.initOverlayView();
29648         
29649         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29650         
29651         var _this = this;
29652                 
29653         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29654             _this.setPosition(_this.gMapContext.marker.position);
29655         });
29656         
29657         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29658             _this.fireEvent('mapClick', this, event);
29659             
29660         });
29661
29662         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29663             _this.fireEvent('mapRightClick', this, event);
29664             
29665         });
29666         
29667         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29668             _this.fireEvent('markerClick', this, event);
29669             
29670         });
29671
29672         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29673             _this.fireEvent('markerRightClick', this, event);
29674             
29675         });
29676         
29677         this.setPosition(this.gMapContext.location);
29678         
29679         this.fireEvent('initial', this, this.gMapContext.location);
29680     },
29681     
29682     initOverlayView: function()
29683     {
29684         var _this = this;
29685         
29686         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29687             
29688             draw: function()
29689             {
29690                 _this.fireEvent('OverlayViewDraw', _this);
29691             },
29692             
29693             onAdd: function()
29694             {
29695                 _this.fireEvent('OverlayViewOnAdd', _this);
29696             },
29697             
29698             onRemove: function()
29699             {
29700                 _this.fireEvent('OverlayViewOnRemove', _this);
29701             },
29702             
29703             show: function(cpx)
29704             {
29705                 _this.fireEvent('OverlayViewShow', _this, cpx);
29706             },
29707             
29708             hide: function()
29709             {
29710                 _this.fireEvent('OverlayViewHide', _this);
29711             }
29712             
29713         });
29714     },
29715     
29716     fromLatLngToContainerPixel: function(event)
29717     {
29718         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29719     },
29720     
29721     isApplied: function() 
29722     {
29723         return this.getGmapContext() == false ? false : true;
29724     },
29725     
29726     getGmapContext: function() 
29727     {
29728         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29729     },
29730     
29731     GMapContext: function() 
29732     {
29733         var position = new google.maps.LatLng(this.latitude, this.longitude);
29734         
29735         var _map = new google.maps.Map(this.el.dom, {
29736             center: position,
29737             zoom: this.zoom,
29738             mapTypeId: this.mapTypeId,
29739             mapTypeControl: this.mapTypeControl,
29740             disableDoubleClickZoom: this.disableDoubleClickZoom,
29741             scrollwheel: this.scrollwheel,
29742             streetViewControl: this.streetViewControl,
29743             locationName: this.locationName,
29744             draggable: this.draggable,
29745             enableAutocomplete: this.enableAutocomplete,
29746             enableReverseGeocode: this.enableReverseGeocode
29747         });
29748         
29749         var _marker = new google.maps.Marker({
29750             position: position,
29751             map: _map,
29752             title: this.markerTitle,
29753             draggable: this.draggable
29754         });
29755         
29756         return {
29757             map: _map,
29758             marker: _marker,
29759             circle: null,
29760             location: position,
29761             radius: this.radius,
29762             locationName: this.locationName,
29763             addressComponents: {
29764                 formatted_address: null,
29765                 addressLine1: null,
29766                 addressLine2: null,
29767                 streetName: null,
29768                 streetNumber: null,
29769                 city: null,
29770                 district: null,
29771                 state: null,
29772                 stateOrProvince: null
29773             },
29774             settings: this,
29775             domContainer: this.el.dom,
29776             geodecoder: new google.maps.Geocoder()
29777         };
29778     },
29779     
29780     drawCircle: function(center, radius, options) 
29781     {
29782         if (this.gMapContext.circle != null) {
29783             this.gMapContext.circle.setMap(null);
29784         }
29785         if (radius > 0) {
29786             radius *= 1;
29787             options = Roo.apply({}, options, {
29788                 strokeColor: "#0000FF",
29789                 strokeOpacity: .35,
29790                 strokeWeight: 2,
29791                 fillColor: "#0000FF",
29792                 fillOpacity: .2
29793             });
29794             
29795             options.map = this.gMapContext.map;
29796             options.radius = radius;
29797             options.center = center;
29798             this.gMapContext.circle = new google.maps.Circle(options);
29799             return this.gMapContext.circle;
29800         }
29801         
29802         return null;
29803     },
29804     
29805     setPosition: function(location) 
29806     {
29807         this.gMapContext.location = location;
29808         this.gMapContext.marker.setPosition(location);
29809         this.gMapContext.map.panTo(location);
29810         this.drawCircle(location, this.gMapContext.radius, {});
29811         
29812         var _this = this;
29813         
29814         if (this.gMapContext.settings.enableReverseGeocode) {
29815             this.gMapContext.geodecoder.geocode({
29816                 latLng: this.gMapContext.location
29817             }, function(results, status) {
29818                 
29819                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29820                     _this.gMapContext.locationName = results[0].formatted_address;
29821                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29822                     
29823                     _this.fireEvent('positionchanged', this, location);
29824                 }
29825             });
29826             
29827             return;
29828         }
29829         
29830         this.fireEvent('positionchanged', this, location);
29831     },
29832     
29833     resize: function()
29834     {
29835         google.maps.event.trigger(this.gMapContext.map, "resize");
29836         
29837         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29838         
29839         this.fireEvent('resize', this);
29840     },
29841     
29842     setPositionByLatLng: function(latitude, longitude)
29843     {
29844         this.setPosition(new google.maps.LatLng(latitude, longitude));
29845     },
29846     
29847     getCurrentPosition: function() 
29848     {
29849         return {
29850             latitude: this.gMapContext.location.lat(),
29851             longitude: this.gMapContext.location.lng()
29852         };
29853     },
29854     
29855     getAddressName: function() 
29856     {
29857         return this.gMapContext.locationName;
29858     },
29859     
29860     getAddressComponents: function() 
29861     {
29862         return this.gMapContext.addressComponents;
29863     },
29864     
29865     address_component_from_google_geocode: function(address_components) 
29866     {
29867         var result = {};
29868         
29869         for (var i = 0; i < address_components.length; i++) {
29870             var component = address_components[i];
29871             if (component.types.indexOf("postal_code") >= 0) {
29872                 result.postalCode = component.short_name;
29873             } else if (component.types.indexOf("street_number") >= 0) {
29874                 result.streetNumber = component.short_name;
29875             } else if (component.types.indexOf("route") >= 0) {
29876                 result.streetName = component.short_name;
29877             } else if (component.types.indexOf("neighborhood") >= 0) {
29878                 result.city = component.short_name;
29879             } else if (component.types.indexOf("locality") >= 0) {
29880                 result.city = component.short_name;
29881             } else if (component.types.indexOf("sublocality") >= 0) {
29882                 result.district = component.short_name;
29883             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29884                 result.stateOrProvince = component.short_name;
29885             } else if (component.types.indexOf("country") >= 0) {
29886                 result.country = component.short_name;
29887             }
29888         }
29889         
29890         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29891         result.addressLine2 = "";
29892         return result;
29893     },
29894     
29895     setZoomLevel: function(zoom)
29896     {
29897         this.gMapContext.map.setZoom(zoom);
29898     },
29899     
29900     show: function()
29901     {
29902         if(!this.el){
29903             return;
29904         }
29905         
29906         this.el.show();
29907         
29908         this.resize();
29909         
29910         this.fireEvent('show', this);
29911     },
29912     
29913     hide: function()
29914     {
29915         if(!this.el){
29916             return;
29917         }
29918         
29919         this.el.hide();
29920         
29921         this.fireEvent('hide', this);
29922     }
29923     
29924 });
29925
29926 Roo.apply(Roo.bootstrap.LocationPicker, {
29927     
29928     OverlayView : function(map, options)
29929     {
29930         options = options || {};
29931         
29932         this.setMap(map);
29933     }
29934     
29935     
29936 });/**
29937  * @class Roo.bootstrap.Alert
29938  * @extends Roo.bootstrap.Component
29939  * Bootstrap Alert class - shows an alert area box
29940  * eg
29941  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29942   Enter a valid email address
29943 </div>
29944  * @licence LGPL
29945  * @cfg {String} title The title of alert
29946  * @cfg {String} html The content of alert
29947  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29948  * @cfg {String} fa font-awesomeicon
29949  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29950  * @cfg {Boolean} close true to show a x closer
29951  * 
29952  * 
29953  * @constructor
29954  * Create a new alert
29955  * @param {Object} config The config object
29956  */
29957
29958
29959 Roo.bootstrap.Alert = function(config){
29960     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29961     
29962 };
29963
29964 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29965     
29966     title: '',
29967     html: '',
29968     weight: false,
29969     fa: false,
29970     faicon: false, // BC
29971     close : false,
29972     
29973     
29974     getAutoCreate : function()
29975     {
29976         
29977         var cfg = {
29978             tag : 'div',
29979             cls : 'alert',
29980             cn : [
29981                 {
29982                     tag: 'button',
29983                     type :  "button",
29984                     cls: "close",
29985                     html : '×',
29986                     style : this.close ? '' : 'display:none'
29987                 },
29988                 {
29989                     tag : 'i',
29990                     cls : 'roo-alert-icon'
29991                     
29992                 },
29993                 {
29994                     tag : 'b',
29995                     cls : 'roo-alert-title',
29996                     html : this.title
29997                 },
29998                 {
29999                     tag : 'span',
30000                     cls : 'roo-alert-text',
30001                     html : this.html
30002                 }
30003             ]
30004         };
30005         
30006         if(this.faicon){
30007             cfg.cn[0].cls += ' fa ' + this.faicon;
30008         }
30009         if(this.fa){
30010             cfg.cn[0].cls += ' fa ' + this.fa;
30011         }
30012         
30013         if(this.weight){
30014             cfg.cls += ' alert-' + this.weight;
30015         }
30016         
30017         return cfg;
30018     },
30019     
30020     initEvents: function() 
30021     {
30022         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30023         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30024         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30025         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30026         if (this.seconds > 0) {
30027             this.hide.defer(this.seconds, this);
30028         }
30029     },
30030     /**
30031      * Set the Title Message HTML
30032      * @param {String} html
30033      */
30034     setTitle : function(str)
30035     {
30036         this.titleEl.dom.innerHTML = str;
30037     },
30038      
30039      /**
30040      * Set the Body Message HTML
30041      * @param {String} html
30042      */
30043     setHtml : function(str)
30044     {
30045         this.htmlEl.dom.innerHTML = str;
30046     },
30047     /**
30048      * Set the Weight of the alert
30049      * @param {String} (success|info|warning|danger) weight
30050      */
30051     
30052     setWeight : function(weight)
30053     {
30054         if(this.weight){
30055             this.el.removeClass('alert-' + this.weight);
30056         }
30057         
30058         this.weight = weight;
30059         
30060         this.el.addClass('alert-' + this.weight);
30061     },
30062       /**
30063      * Set the Icon of the alert
30064      * @param {String} see fontawsome names (name without the 'fa-' bit)
30065      */
30066     setIcon : function(icon)
30067     {
30068         if(this.faicon){
30069             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30070         }
30071         
30072         this.faicon = icon;
30073         
30074         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30075     },
30076     /**
30077      * Hide the Alert
30078      */
30079     hide: function() 
30080     {
30081         this.el.hide();   
30082     },
30083     /**
30084      * Show the Alert
30085      */
30086     show: function() 
30087     {  
30088         this.el.show();   
30089     }
30090     
30091 });
30092
30093  
30094 /*
30095 * Licence: LGPL
30096 */
30097
30098 /**
30099  * @class Roo.bootstrap.UploadCropbox
30100  * @extends Roo.bootstrap.Component
30101  * Bootstrap UploadCropbox class
30102  * @cfg {String} emptyText show when image has been loaded
30103  * @cfg {String} rotateNotify show when image too small to rotate
30104  * @cfg {Number} errorTimeout default 3000
30105  * @cfg {Number} minWidth default 300
30106  * @cfg {Number} minHeight default 300
30107  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30108  * @cfg {Boolean} isDocument (true|false) default false
30109  * @cfg {String} url action url
30110  * @cfg {String} paramName default 'imageUpload'
30111  * @cfg {String} method default POST
30112  * @cfg {Boolean} loadMask (true|false) default true
30113  * @cfg {Boolean} loadingText default 'Loading...'
30114  * 
30115  * @constructor
30116  * Create a new UploadCropbox
30117  * @param {Object} config The config object
30118  */
30119
30120 Roo.bootstrap.UploadCropbox = function(config){
30121     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30122     
30123     this.addEvents({
30124         /**
30125          * @event beforeselectfile
30126          * Fire before select file
30127          * @param {Roo.bootstrap.UploadCropbox} this
30128          */
30129         "beforeselectfile" : true,
30130         /**
30131          * @event initial
30132          * Fire after initEvent
30133          * @param {Roo.bootstrap.UploadCropbox} this
30134          */
30135         "initial" : true,
30136         /**
30137          * @event crop
30138          * Fire after initEvent
30139          * @param {Roo.bootstrap.UploadCropbox} this
30140          * @param {String} data
30141          */
30142         "crop" : true,
30143         /**
30144          * @event prepare
30145          * Fire when preparing the file data
30146          * @param {Roo.bootstrap.UploadCropbox} this
30147          * @param {Object} file
30148          */
30149         "prepare" : true,
30150         /**
30151          * @event exception
30152          * Fire when get exception
30153          * @param {Roo.bootstrap.UploadCropbox} this
30154          * @param {XMLHttpRequest} xhr
30155          */
30156         "exception" : true,
30157         /**
30158          * @event beforeloadcanvas
30159          * Fire before load the canvas
30160          * @param {Roo.bootstrap.UploadCropbox} this
30161          * @param {String} src
30162          */
30163         "beforeloadcanvas" : true,
30164         /**
30165          * @event trash
30166          * Fire when trash image
30167          * @param {Roo.bootstrap.UploadCropbox} this
30168          */
30169         "trash" : true,
30170         /**
30171          * @event download
30172          * Fire when download the image
30173          * @param {Roo.bootstrap.UploadCropbox} this
30174          */
30175         "download" : true,
30176         /**
30177          * @event footerbuttonclick
30178          * Fire when footerbuttonclick
30179          * @param {Roo.bootstrap.UploadCropbox} this
30180          * @param {String} type
30181          */
30182         "footerbuttonclick" : true,
30183         /**
30184          * @event resize
30185          * Fire when resize
30186          * @param {Roo.bootstrap.UploadCropbox} this
30187          */
30188         "resize" : true,
30189         /**
30190          * @event rotate
30191          * Fire when rotate the image
30192          * @param {Roo.bootstrap.UploadCropbox} this
30193          * @param {String} pos
30194          */
30195         "rotate" : true,
30196         /**
30197          * @event inspect
30198          * Fire when inspect the file
30199          * @param {Roo.bootstrap.UploadCropbox} this
30200          * @param {Object} file
30201          */
30202         "inspect" : true,
30203         /**
30204          * @event upload
30205          * Fire when xhr upload the file
30206          * @param {Roo.bootstrap.UploadCropbox} this
30207          * @param {Object} data
30208          */
30209         "upload" : true,
30210         /**
30211          * @event arrange
30212          * Fire when arrange the file data
30213          * @param {Roo.bootstrap.UploadCropbox} this
30214          * @param {Object} formData
30215          */
30216         "arrange" : true
30217     });
30218     
30219     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30220 };
30221
30222 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30223     
30224     emptyText : 'Click to upload image',
30225     rotateNotify : 'Image is too small to rotate',
30226     errorTimeout : 3000,
30227     scale : 0,
30228     baseScale : 1,
30229     rotate : 0,
30230     dragable : false,
30231     pinching : false,
30232     mouseX : 0,
30233     mouseY : 0,
30234     cropData : false,
30235     minWidth : 300,
30236     minHeight : 300,
30237     file : false,
30238     exif : {},
30239     baseRotate : 1,
30240     cropType : 'image/jpeg',
30241     buttons : false,
30242     canvasLoaded : false,
30243     isDocument : false,
30244     method : 'POST',
30245     paramName : 'imageUpload',
30246     loadMask : true,
30247     loadingText : 'Loading...',
30248     maskEl : false,
30249     
30250     getAutoCreate : function()
30251     {
30252         var cfg = {
30253             tag : 'div',
30254             cls : 'roo-upload-cropbox',
30255             cn : [
30256                 {
30257                     tag : 'input',
30258                     cls : 'roo-upload-cropbox-selector',
30259                     type : 'file'
30260                 },
30261                 {
30262                     tag : 'div',
30263                     cls : 'roo-upload-cropbox-body',
30264                     style : 'cursor:pointer',
30265                     cn : [
30266                         {
30267                             tag : 'div',
30268                             cls : 'roo-upload-cropbox-preview'
30269                         },
30270                         {
30271                             tag : 'div',
30272                             cls : 'roo-upload-cropbox-thumb'
30273                         },
30274                         {
30275                             tag : 'div',
30276                             cls : 'roo-upload-cropbox-empty-notify',
30277                             html : this.emptyText
30278                         },
30279                         {
30280                             tag : 'div',
30281                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30282                             html : this.rotateNotify
30283                         }
30284                     ]
30285                 },
30286                 {
30287                     tag : 'div',
30288                     cls : 'roo-upload-cropbox-footer',
30289                     cn : {
30290                         tag : 'div',
30291                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30292                         cn : []
30293                     }
30294                 }
30295             ]
30296         };
30297         
30298         return cfg;
30299     },
30300     
30301     onRender : function(ct, position)
30302     {
30303         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30304         
30305         if (this.buttons.length) {
30306             
30307             Roo.each(this.buttons, function(bb) {
30308                 
30309                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30310                 
30311                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30312                 
30313             }, this);
30314         }
30315         
30316         if(this.loadMask){
30317             this.maskEl = this.el;
30318         }
30319     },
30320     
30321     initEvents : function()
30322     {
30323         this.urlAPI = (window.createObjectURL && window) || 
30324                                 (window.URL && URL.revokeObjectURL && URL) || 
30325                                 (window.webkitURL && webkitURL);
30326                         
30327         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30328         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30329         
30330         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30331         this.selectorEl.hide();
30332         
30333         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30334         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30335         
30336         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30337         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30338         this.thumbEl.hide();
30339         
30340         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30341         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30342         
30343         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30344         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30345         this.errorEl.hide();
30346         
30347         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30348         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30349         this.footerEl.hide();
30350         
30351         this.setThumbBoxSize();
30352         
30353         this.bind();
30354         
30355         this.resize();
30356         
30357         this.fireEvent('initial', this);
30358     },
30359
30360     bind : function()
30361     {
30362         var _this = this;
30363         
30364         window.addEventListener("resize", function() { _this.resize(); } );
30365         
30366         this.bodyEl.on('click', this.beforeSelectFile, this);
30367         
30368         if(Roo.isTouch){
30369             this.bodyEl.on('touchstart', this.onTouchStart, this);
30370             this.bodyEl.on('touchmove', this.onTouchMove, this);
30371             this.bodyEl.on('touchend', this.onTouchEnd, this);
30372         }
30373         
30374         if(!Roo.isTouch){
30375             this.bodyEl.on('mousedown', this.onMouseDown, this);
30376             this.bodyEl.on('mousemove', this.onMouseMove, this);
30377             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30378             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30379             Roo.get(document).on('mouseup', this.onMouseUp, this);
30380         }
30381         
30382         this.selectorEl.on('change', this.onFileSelected, this);
30383     },
30384     
30385     reset : function()
30386     {    
30387         this.scale = 0;
30388         this.baseScale = 1;
30389         this.rotate = 0;
30390         this.baseRotate = 1;
30391         this.dragable = false;
30392         this.pinching = false;
30393         this.mouseX = 0;
30394         this.mouseY = 0;
30395         this.cropData = false;
30396         this.notifyEl.dom.innerHTML = this.emptyText;
30397         
30398         this.selectorEl.dom.value = '';
30399         
30400     },
30401     
30402     resize : function()
30403     {
30404         if(this.fireEvent('resize', this) != false){
30405             this.setThumbBoxPosition();
30406             this.setCanvasPosition();
30407         }
30408     },
30409     
30410     onFooterButtonClick : function(e, el, o, type)
30411     {
30412         switch (type) {
30413             case 'rotate-left' :
30414                 this.onRotateLeft(e);
30415                 break;
30416             case 'rotate-right' :
30417                 this.onRotateRight(e);
30418                 break;
30419             case 'picture' :
30420                 this.beforeSelectFile(e);
30421                 break;
30422             case 'trash' :
30423                 this.trash(e);
30424                 break;
30425             case 'crop' :
30426                 this.crop(e);
30427                 break;
30428             case 'download' :
30429                 this.download(e);
30430                 break;
30431             default :
30432                 break;
30433         }
30434         
30435         this.fireEvent('footerbuttonclick', this, type);
30436     },
30437     
30438     beforeSelectFile : function(e)
30439     {
30440         e.preventDefault();
30441         
30442         if(this.fireEvent('beforeselectfile', this) != false){
30443             this.selectorEl.dom.click();
30444         }
30445     },
30446     
30447     onFileSelected : function(e)
30448     {
30449         e.preventDefault();
30450         
30451         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30452             return;
30453         }
30454         
30455         var file = this.selectorEl.dom.files[0];
30456         
30457         if(this.fireEvent('inspect', this, file) != false){
30458             this.prepare(file);
30459         }
30460         
30461     },
30462     
30463     trash : function(e)
30464     {
30465         this.fireEvent('trash', this);
30466     },
30467     
30468     download : function(e)
30469     {
30470         this.fireEvent('download', this);
30471     },
30472     
30473     loadCanvas : function(src)
30474     {   
30475         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30476             
30477             this.reset();
30478             
30479             this.imageEl = document.createElement('img');
30480             
30481             var _this = this;
30482             
30483             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30484             
30485             this.imageEl.src = src;
30486         }
30487     },
30488     
30489     onLoadCanvas : function()
30490     {   
30491         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30492         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30493         
30494         this.bodyEl.un('click', this.beforeSelectFile, this);
30495         
30496         this.notifyEl.hide();
30497         this.thumbEl.show();
30498         this.footerEl.show();
30499         
30500         this.baseRotateLevel();
30501         
30502         if(this.isDocument){
30503             this.setThumbBoxSize();
30504         }
30505         
30506         this.setThumbBoxPosition();
30507         
30508         this.baseScaleLevel();
30509         
30510         this.draw();
30511         
30512         this.resize();
30513         
30514         this.canvasLoaded = true;
30515         
30516         if(this.loadMask){
30517             this.maskEl.unmask();
30518         }
30519         
30520     },
30521     
30522     setCanvasPosition : function()
30523     {   
30524         if(!this.canvasEl){
30525             return;
30526         }
30527         
30528         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30529         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30530         
30531         this.previewEl.setLeft(pw);
30532         this.previewEl.setTop(ph);
30533         
30534     },
30535     
30536     onMouseDown : function(e)
30537     {   
30538         e.stopEvent();
30539         
30540         this.dragable = true;
30541         this.pinching = false;
30542         
30543         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30544             this.dragable = false;
30545             return;
30546         }
30547         
30548         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30549         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30550         
30551     },
30552     
30553     onMouseMove : function(e)
30554     {   
30555         e.stopEvent();
30556         
30557         if(!this.canvasLoaded){
30558             return;
30559         }
30560         
30561         if (!this.dragable){
30562             return;
30563         }
30564         
30565         var minX = Math.ceil(this.thumbEl.getLeft(true));
30566         var minY = Math.ceil(this.thumbEl.getTop(true));
30567         
30568         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30569         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30570         
30571         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30572         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30573         
30574         x = x - this.mouseX;
30575         y = y - this.mouseY;
30576         
30577         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30578         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30579         
30580         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30581         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30582         
30583         this.previewEl.setLeft(bgX);
30584         this.previewEl.setTop(bgY);
30585         
30586         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30587         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30588     },
30589     
30590     onMouseUp : function(e)
30591     {   
30592         e.stopEvent();
30593         
30594         this.dragable = false;
30595     },
30596     
30597     onMouseWheel : function(e)
30598     {   
30599         e.stopEvent();
30600         
30601         this.startScale = this.scale;
30602         
30603         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30604         
30605         if(!this.zoomable()){
30606             this.scale = this.startScale;
30607             return;
30608         }
30609         
30610         this.draw();
30611         
30612         return;
30613     },
30614     
30615     zoomable : function()
30616     {
30617         var minScale = this.thumbEl.getWidth() / this.minWidth;
30618         
30619         if(this.minWidth < this.minHeight){
30620             minScale = this.thumbEl.getHeight() / this.minHeight;
30621         }
30622         
30623         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30624         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30625         
30626         if(
30627                 this.isDocument &&
30628                 (this.rotate == 0 || this.rotate == 180) && 
30629                 (
30630                     width > this.imageEl.OriginWidth || 
30631                     height > this.imageEl.OriginHeight ||
30632                     (width < this.minWidth && height < this.minHeight)
30633                 )
30634         ){
30635             return false;
30636         }
30637         
30638         if(
30639                 this.isDocument &&
30640                 (this.rotate == 90 || this.rotate == 270) && 
30641                 (
30642                     width > this.imageEl.OriginWidth || 
30643                     height > this.imageEl.OriginHeight ||
30644                     (width < this.minHeight && height < this.minWidth)
30645                 )
30646         ){
30647             return false;
30648         }
30649         
30650         if(
30651                 !this.isDocument &&
30652                 (this.rotate == 0 || this.rotate == 180) && 
30653                 (
30654                     width < this.minWidth || 
30655                     width > this.imageEl.OriginWidth || 
30656                     height < this.minHeight || 
30657                     height > this.imageEl.OriginHeight
30658                 )
30659         ){
30660             return false;
30661         }
30662         
30663         if(
30664                 !this.isDocument &&
30665                 (this.rotate == 90 || this.rotate == 270) && 
30666                 (
30667                     width < this.minHeight || 
30668                     width > this.imageEl.OriginWidth || 
30669                     height < this.minWidth || 
30670                     height > this.imageEl.OriginHeight
30671                 )
30672         ){
30673             return false;
30674         }
30675         
30676         return true;
30677         
30678     },
30679     
30680     onRotateLeft : function(e)
30681     {   
30682         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30683             
30684             var minScale = this.thumbEl.getWidth() / this.minWidth;
30685             
30686             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30687             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30688             
30689             this.startScale = this.scale;
30690             
30691             while (this.getScaleLevel() < minScale){
30692             
30693                 this.scale = this.scale + 1;
30694                 
30695                 if(!this.zoomable()){
30696                     break;
30697                 }
30698                 
30699                 if(
30700                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30701                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30702                 ){
30703                     continue;
30704                 }
30705                 
30706                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30707
30708                 this.draw();
30709                 
30710                 return;
30711             }
30712             
30713             this.scale = this.startScale;
30714             
30715             this.onRotateFail();
30716             
30717             return false;
30718         }
30719         
30720         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30721
30722         if(this.isDocument){
30723             this.setThumbBoxSize();
30724             this.setThumbBoxPosition();
30725             this.setCanvasPosition();
30726         }
30727         
30728         this.draw();
30729         
30730         this.fireEvent('rotate', this, 'left');
30731         
30732     },
30733     
30734     onRotateRight : function(e)
30735     {
30736         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30737             
30738             var minScale = this.thumbEl.getWidth() / this.minWidth;
30739         
30740             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30741             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30742             
30743             this.startScale = this.scale;
30744             
30745             while (this.getScaleLevel() < minScale){
30746             
30747                 this.scale = this.scale + 1;
30748                 
30749                 if(!this.zoomable()){
30750                     break;
30751                 }
30752                 
30753                 if(
30754                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30755                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30756                 ){
30757                     continue;
30758                 }
30759                 
30760                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30761
30762                 this.draw();
30763                 
30764                 return;
30765             }
30766             
30767             this.scale = this.startScale;
30768             
30769             this.onRotateFail();
30770             
30771             return false;
30772         }
30773         
30774         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30775
30776         if(this.isDocument){
30777             this.setThumbBoxSize();
30778             this.setThumbBoxPosition();
30779             this.setCanvasPosition();
30780         }
30781         
30782         this.draw();
30783         
30784         this.fireEvent('rotate', this, 'right');
30785     },
30786     
30787     onRotateFail : function()
30788     {
30789         this.errorEl.show(true);
30790         
30791         var _this = this;
30792         
30793         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30794     },
30795     
30796     draw : function()
30797     {
30798         this.previewEl.dom.innerHTML = '';
30799         
30800         var canvasEl = document.createElement("canvas");
30801         
30802         var contextEl = canvasEl.getContext("2d");
30803         
30804         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30805         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30806         var center = this.imageEl.OriginWidth / 2;
30807         
30808         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30809             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30810             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30811             center = this.imageEl.OriginHeight / 2;
30812         }
30813         
30814         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30815         
30816         contextEl.translate(center, center);
30817         contextEl.rotate(this.rotate * Math.PI / 180);
30818
30819         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30820         
30821         this.canvasEl = document.createElement("canvas");
30822         
30823         this.contextEl = this.canvasEl.getContext("2d");
30824         
30825         switch (this.rotate) {
30826             case 0 :
30827                 
30828                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30829                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30830                 
30831                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30832                 
30833                 break;
30834             case 90 : 
30835                 
30836                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30837                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30838                 
30839                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30840                     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);
30841                     break;
30842                 }
30843                 
30844                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30845                 
30846                 break;
30847             case 180 :
30848                 
30849                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30850                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30851                 
30852                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30853                     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);
30854                     break;
30855                 }
30856                 
30857                 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);
30858                 
30859                 break;
30860             case 270 :
30861                 
30862                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30863                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30864         
30865                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30866                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30867                     break;
30868                 }
30869                 
30870                 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);
30871                 
30872                 break;
30873             default : 
30874                 break;
30875         }
30876         
30877         this.previewEl.appendChild(this.canvasEl);
30878         
30879         this.setCanvasPosition();
30880     },
30881     
30882     crop : function()
30883     {
30884         if(!this.canvasLoaded){
30885             return;
30886         }
30887         
30888         var imageCanvas = document.createElement("canvas");
30889         
30890         var imageContext = imageCanvas.getContext("2d");
30891         
30892         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30893         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30894         
30895         var center = imageCanvas.width / 2;
30896         
30897         imageContext.translate(center, center);
30898         
30899         imageContext.rotate(this.rotate * Math.PI / 180);
30900         
30901         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30902         
30903         var canvas = document.createElement("canvas");
30904         
30905         var context = canvas.getContext("2d");
30906                 
30907         canvas.width = this.minWidth;
30908         canvas.height = this.minHeight;
30909
30910         switch (this.rotate) {
30911             case 0 :
30912                 
30913                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30914                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30915                 
30916                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30917                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30918                 
30919                 var targetWidth = this.minWidth - 2 * x;
30920                 var targetHeight = this.minHeight - 2 * y;
30921                 
30922                 var scale = 1;
30923                 
30924                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30925                     scale = targetWidth / width;
30926                 }
30927                 
30928                 if(x > 0 && y == 0){
30929                     scale = targetHeight / height;
30930                 }
30931                 
30932                 if(x > 0 && y > 0){
30933                     scale = targetWidth / width;
30934                     
30935                     if(width < height){
30936                         scale = targetHeight / height;
30937                     }
30938                 }
30939                 
30940                 context.scale(scale, scale);
30941                 
30942                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30943                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30944
30945                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30946                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30947
30948                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30949                 
30950                 break;
30951             case 90 : 
30952                 
30953                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30954                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30955                 
30956                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30957                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30958                 
30959                 var targetWidth = this.minWidth - 2 * x;
30960                 var targetHeight = this.minHeight - 2 * y;
30961                 
30962                 var scale = 1;
30963                 
30964                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30965                     scale = targetWidth / width;
30966                 }
30967                 
30968                 if(x > 0 && y == 0){
30969                     scale = targetHeight / height;
30970                 }
30971                 
30972                 if(x > 0 && y > 0){
30973                     scale = targetWidth / width;
30974                     
30975                     if(width < height){
30976                         scale = targetHeight / height;
30977                     }
30978                 }
30979                 
30980                 context.scale(scale, scale);
30981                 
30982                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30983                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30984
30985                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30986                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30987                 
30988                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30989                 
30990                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30991                 
30992                 break;
30993             case 180 :
30994                 
30995                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30996                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30997                 
30998                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30999                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31000                 
31001                 var targetWidth = this.minWidth - 2 * x;
31002                 var targetHeight = this.minHeight - 2 * y;
31003                 
31004                 var scale = 1;
31005                 
31006                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31007                     scale = targetWidth / width;
31008                 }
31009                 
31010                 if(x > 0 && y == 0){
31011                     scale = targetHeight / height;
31012                 }
31013                 
31014                 if(x > 0 && y > 0){
31015                     scale = targetWidth / width;
31016                     
31017                     if(width < height){
31018                         scale = targetHeight / height;
31019                     }
31020                 }
31021                 
31022                 context.scale(scale, scale);
31023                 
31024                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31025                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31026
31027                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31028                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31029
31030                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31031                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31032                 
31033                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31034                 
31035                 break;
31036             case 270 :
31037                 
31038                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31039                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31040                 
31041                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31042                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31043                 
31044                 var targetWidth = this.minWidth - 2 * x;
31045                 var targetHeight = this.minHeight - 2 * y;
31046                 
31047                 var scale = 1;
31048                 
31049                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31050                     scale = targetWidth / width;
31051                 }
31052                 
31053                 if(x > 0 && y == 0){
31054                     scale = targetHeight / height;
31055                 }
31056                 
31057                 if(x > 0 && y > 0){
31058                     scale = targetWidth / width;
31059                     
31060                     if(width < height){
31061                         scale = targetHeight / height;
31062                     }
31063                 }
31064                 
31065                 context.scale(scale, scale);
31066                 
31067                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31068                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31069
31070                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31071                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31072                 
31073                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31074                 
31075                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31076                 
31077                 break;
31078             default : 
31079                 break;
31080         }
31081         
31082         this.cropData = canvas.toDataURL(this.cropType);
31083         
31084         if(this.fireEvent('crop', this, this.cropData) !== false){
31085             this.process(this.file, this.cropData);
31086         }
31087         
31088         return;
31089         
31090     },
31091     
31092     setThumbBoxSize : function()
31093     {
31094         var width, height;
31095         
31096         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31097             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31098             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31099             
31100             this.minWidth = width;
31101             this.minHeight = height;
31102             
31103             if(this.rotate == 90 || this.rotate == 270){
31104                 this.minWidth = height;
31105                 this.minHeight = width;
31106             }
31107         }
31108         
31109         height = 300;
31110         width = Math.ceil(this.minWidth * height / this.minHeight);
31111         
31112         if(this.minWidth > this.minHeight){
31113             width = 300;
31114             height = Math.ceil(this.minHeight * width / this.minWidth);
31115         }
31116         
31117         this.thumbEl.setStyle({
31118             width : width + 'px',
31119             height : height + 'px'
31120         });
31121
31122         return;
31123             
31124     },
31125     
31126     setThumbBoxPosition : function()
31127     {
31128         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31129         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31130         
31131         this.thumbEl.setLeft(x);
31132         this.thumbEl.setTop(y);
31133         
31134     },
31135     
31136     baseRotateLevel : function()
31137     {
31138         this.baseRotate = 1;
31139         
31140         if(
31141                 typeof(this.exif) != 'undefined' &&
31142                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31143                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31144         ){
31145             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31146         }
31147         
31148         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31149         
31150     },
31151     
31152     baseScaleLevel : function()
31153     {
31154         var width, height;
31155         
31156         if(this.isDocument){
31157             
31158             if(this.baseRotate == 6 || this.baseRotate == 8){
31159             
31160                 height = this.thumbEl.getHeight();
31161                 this.baseScale = height / this.imageEl.OriginWidth;
31162
31163                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31164                     width = this.thumbEl.getWidth();
31165                     this.baseScale = width / this.imageEl.OriginHeight;
31166                 }
31167
31168                 return;
31169             }
31170
31171             height = this.thumbEl.getHeight();
31172             this.baseScale = height / this.imageEl.OriginHeight;
31173
31174             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31175                 width = this.thumbEl.getWidth();
31176                 this.baseScale = width / this.imageEl.OriginWidth;
31177             }
31178
31179             return;
31180         }
31181         
31182         if(this.baseRotate == 6 || this.baseRotate == 8){
31183             
31184             width = this.thumbEl.getHeight();
31185             this.baseScale = width / this.imageEl.OriginHeight;
31186             
31187             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31188                 height = this.thumbEl.getWidth();
31189                 this.baseScale = height / this.imageEl.OriginHeight;
31190             }
31191             
31192             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31193                 height = this.thumbEl.getWidth();
31194                 this.baseScale = height / this.imageEl.OriginHeight;
31195                 
31196                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31197                     width = this.thumbEl.getHeight();
31198                     this.baseScale = width / this.imageEl.OriginWidth;
31199                 }
31200             }
31201             
31202             return;
31203         }
31204         
31205         width = this.thumbEl.getWidth();
31206         this.baseScale = width / this.imageEl.OriginWidth;
31207         
31208         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31209             height = this.thumbEl.getHeight();
31210             this.baseScale = height / this.imageEl.OriginHeight;
31211         }
31212         
31213         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31214             
31215             height = this.thumbEl.getHeight();
31216             this.baseScale = height / this.imageEl.OriginHeight;
31217             
31218             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31219                 width = this.thumbEl.getWidth();
31220                 this.baseScale = width / this.imageEl.OriginWidth;
31221             }
31222             
31223         }
31224         
31225         return;
31226     },
31227     
31228     getScaleLevel : function()
31229     {
31230         return this.baseScale * Math.pow(1.1, this.scale);
31231     },
31232     
31233     onTouchStart : function(e)
31234     {
31235         if(!this.canvasLoaded){
31236             this.beforeSelectFile(e);
31237             return;
31238         }
31239         
31240         var touches = e.browserEvent.touches;
31241         
31242         if(!touches){
31243             return;
31244         }
31245         
31246         if(touches.length == 1){
31247             this.onMouseDown(e);
31248             return;
31249         }
31250         
31251         if(touches.length != 2){
31252             return;
31253         }
31254         
31255         var coords = [];
31256         
31257         for(var i = 0, finger; finger = touches[i]; i++){
31258             coords.push(finger.pageX, finger.pageY);
31259         }
31260         
31261         var x = Math.pow(coords[0] - coords[2], 2);
31262         var y = Math.pow(coords[1] - coords[3], 2);
31263         
31264         this.startDistance = Math.sqrt(x + y);
31265         
31266         this.startScale = this.scale;
31267         
31268         this.pinching = true;
31269         this.dragable = false;
31270         
31271     },
31272     
31273     onTouchMove : function(e)
31274     {
31275         if(!this.pinching && !this.dragable){
31276             return;
31277         }
31278         
31279         var touches = e.browserEvent.touches;
31280         
31281         if(!touches){
31282             return;
31283         }
31284         
31285         if(this.dragable){
31286             this.onMouseMove(e);
31287             return;
31288         }
31289         
31290         var coords = [];
31291         
31292         for(var i = 0, finger; finger = touches[i]; i++){
31293             coords.push(finger.pageX, finger.pageY);
31294         }
31295         
31296         var x = Math.pow(coords[0] - coords[2], 2);
31297         var y = Math.pow(coords[1] - coords[3], 2);
31298         
31299         this.endDistance = Math.sqrt(x + y);
31300         
31301         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31302         
31303         if(!this.zoomable()){
31304             this.scale = this.startScale;
31305             return;
31306         }
31307         
31308         this.draw();
31309         
31310     },
31311     
31312     onTouchEnd : function(e)
31313     {
31314         this.pinching = false;
31315         this.dragable = false;
31316         
31317     },
31318     
31319     process : function(file, crop)
31320     {
31321         if(this.loadMask){
31322             this.maskEl.mask(this.loadingText);
31323         }
31324         
31325         this.xhr = new XMLHttpRequest();
31326         
31327         file.xhr = this.xhr;
31328
31329         this.xhr.open(this.method, this.url, true);
31330         
31331         var headers = {
31332             "Accept": "application/json",
31333             "Cache-Control": "no-cache",
31334             "X-Requested-With": "XMLHttpRequest"
31335         };
31336         
31337         for (var headerName in headers) {
31338             var headerValue = headers[headerName];
31339             if (headerValue) {
31340                 this.xhr.setRequestHeader(headerName, headerValue);
31341             }
31342         }
31343         
31344         var _this = this;
31345         
31346         this.xhr.onload = function()
31347         {
31348             _this.xhrOnLoad(_this.xhr);
31349         }
31350         
31351         this.xhr.onerror = function()
31352         {
31353             _this.xhrOnError(_this.xhr);
31354         }
31355         
31356         var formData = new FormData();
31357
31358         formData.append('returnHTML', 'NO');
31359         
31360         if(crop){
31361             formData.append('crop', crop);
31362         }
31363         
31364         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31365             formData.append(this.paramName, file, file.name);
31366         }
31367         
31368         if(typeof(file.filename) != 'undefined'){
31369             formData.append('filename', file.filename);
31370         }
31371         
31372         if(typeof(file.mimetype) != 'undefined'){
31373             formData.append('mimetype', file.mimetype);
31374         }
31375         
31376         if(this.fireEvent('arrange', this, formData) != false){
31377             this.xhr.send(formData);
31378         };
31379     },
31380     
31381     xhrOnLoad : function(xhr)
31382     {
31383         if(this.loadMask){
31384             this.maskEl.unmask();
31385         }
31386         
31387         if (xhr.readyState !== 4) {
31388             this.fireEvent('exception', this, xhr);
31389             return;
31390         }
31391
31392         var response = Roo.decode(xhr.responseText);
31393         
31394         if(!response.success){
31395             this.fireEvent('exception', this, xhr);
31396             return;
31397         }
31398         
31399         var response = Roo.decode(xhr.responseText);
31400         
31401         this.fireEvent('upload', this, response);
31402         
31403     },
31404     
31405     xhrOnError : function()
31406     {
31407         if(this.loadMask){
31408             this.maskEl.unmask();
31409         }
31410         
31411         Roo.log('xhr on error');
31412         
31413         var response = Roo.decode(xhr.responseText);
31414           
31415         Roo.log(response);
31416         
31417     },
31418     
31419     prepare : function(file)
31420     {   
31421         if(this.loadMask){
31422             this.maskEl.mask(this.loadingText);
31423         }
31424         
31425         this.file = false;
31426         this.exif = {};
31427         
31428         if(typeof(file) === 'string'){
31429             this.loadCanvas(file);
31430             return;
31431         }
31432         
31433         if(!file || !this.urlAPI){
31434             return;
31435         }
31436         
31437         this.file = file;
31438         this.cropType = file.type;
31439         
31440         var _this = this;
31441         
31442         if(this.fireEvent('prepare', this, this.file) != false){
31443             
31444             var reader = new FileReader();
31445             
31446             reader.onload = function (e) {
31447                 if (e.target.error) {
31448                     Roo.log(e.target.error);
31449                     return;
31450                 }
31451                 
31452                 var buffer = e.target.result,
31453                     dataView = new DataView(buffer),
31454                     offset = 2,
31455                     maxOffset = dataView.byteLength - 4,
31456                     markerBytes,
31457                     markerLength;
31458                 
31459                 if (dataView.getUint16(0) === 0xffd8) {
31460                     while (offset < maxOffset) {
31461                         markerBytes = dataView.getUint16(offset);
31462                         
31463                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31464                             markerLength = dataView.getUint16(offset + 2) + 2;
31465                             if (offset + markerLength > dataView.byteLength) {
31466                                 Roo.log('Invalid meta data: Invalid segment size.');
31467                                 break;
31468                             }
31469                             
31470                             if(markerBytes == 0xffe1){
31471                                 _this.parseExifData(
31472                                     dataView,
31473                                     offset,
31474                                     markerLength
31475                                 );
31476                             }
31477                             
31478                             offset += markerLength;
31479                             
31480                             continue;
31481                         }
31482                         
31483                         break;
31484                     }
31485                     
31486                 }
31487                 
31488                 var url = _this.urlAPI.createObjectURL(_this.file);
31489                 
31490                 _this.loadCanvas(url);
31491                 
31492                 return;
31493             }
31494             
31495             reader.readAsArrayBuffer(this.file);
31496             
31497         }
31498         
31499     },
31500     
31501     parseExifData : function(dataView, offset, length)
31502     {
31503         var tiffOffset = offset + 10,
31504             littleEndian,
31505             dirOffset;
31506     
31507         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31508             // No Exif data, might be XMP data instead
31509             return;
31510         }
31511         
31512         // Check for the ASCII code for "Exif" (0x45786966):
31513         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31514             // No Exif data, might be XMP data instead
31515             return;
31516         }
31517         if (tiffOffset + 8 > dataView.byteLength) {
31518             Roo.log('Invalid Exif data: Invalid segment size.');
31519             return;
31520         }
31521         // Check for the two null bytes:
31522         if (dataView.getUint16(offset + 8) !== 0x0000) {
31523             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31524             return;
31525         }
31526         // Check the byte alignment:
31527         switch (dataView.getUint16(tiffOffset)) {
31528         case 0x4949:
31529             littleEndian = true;
31530             break;
31531         case 0x4D4D:
31532             littleEndian = false;
31533             break;
31534         default:
31535             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31536             return;
31537         }
31538         // Check for the TIFF tag marker (0x002A):
31539         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31540             Roo.log('Invalid Exif data: Missing TIFF marker.');
31541             return;
31542         }
31543         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31544         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31545         
31546         this.parseExifTags(
31547             dataView,
31548             tiffOffset,
31549             tiffOffset + dirOffset,
31550             littleEndian
31551         );
31552     },
31553     
31554     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31555     {
31556         var tagsNumber,
31557             dirEndOffset,
31558             i;
31559         if (dirOffset + 6 > dataView.byteLength) {
31560             Roo.log('Invalid Exif data: Invalid directory offset.');
31561             return;
31562         }
31563         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31564         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31565         if (dirEndOffset + 4 > dataView.byteLength) {
31566             Roo.log('Invalid Exif data: Invalid directory size.');
31567             return;
31568         }
31569         for (i = 0; i < tagsNumber; i += 1) {
31570             this.parseExifTag(
31571                 dataView,
31572                 tiffOffset,
31573                 dirOffset + 2 + 12 * i, // tag offset
31574                 littleEndian
31575             );
31576         }
31577         // Return the offset to the next directory:
31578         return dataView.getUint32(dirEndOffset, littleEndian);
31579     },
31580     
31581     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31582     {
31583         var tag = dataView.getUint16(offset, littleEndian);
31584         
31585         this.exif[tag] = this.getExifValue(
31586             dataView,
31587             tiffOffset,
31588             offset,
31589             dataView.getUint16(offset + 2, littleEndian), // tag type
31590             dataView.getUint32(offset + 4, littleEndian), // tag length
31591             littleEndian
31592         );
31593     },
31594     
31595     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31596     {
31597         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31598             tagSize,
31599             dataOffset,
31600             values,
31601             i,
31602             str,
31603             c;
31604     
31605         if (!tagType) {
31606             Roo.log('Invalid Exif data: Invalid tag type.');
31607             return;
31608         }
31609         
31610         tagSize = tagType.size * length;
31611         // Determine if the value is contained in the dataOffset bytes,
31612         // or if the value at the dataOffset is a pointer to the actual data:
31613         dataOffset = tagSize > 4 ?
31614                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31615         if (dataOffset + tagSize > dataView.byteLength) {
31616             Roo.log('Invalid Exif data: Invalid data offset.');
31617             return;
31618         }
31619         if (length === 1) {
31620             return tagType.getValue(dataView, dataOffset, littleEndian);
31621         }
31622         values = [];
31623         for (i = 0; i < length; i += 1) {
31624             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31625         }
31626         
31627         if (tagType.ascii) {
31628             str = '';
31629             // Concatenate the chars:
31630             for (i = 0; i < values.length; i += 1) {
31631                 c = values[i];
31632                 // Ignore the terminating NULL byte(s):
31633                 if (c === '\u0000') {
31634                     break;
31635                 }
31636                 str += c;
31637             }
31638             return str;
31639         }
31640         return values;
31641     }
31642     
31643 });
31644
31645 Roo.apply(Roo.bootstrap.UploadCropbox, {
31646     tags : {
31647         'Orientation': 0x0112
31648     },
31649     
31650     Orientation: {
31651             1: 0, //'top-left',
31652 //            2: 'top-right',
31653             3: 180, //'bottom-right',
31654 //            4: 'bottom-left',
31655 //            5: 'left-top',
31656             6: 90, //'right-top',
31657 //            7: 'right-bottom',
31658             8: 270 //'left-bottom'
31659     },
31660     
31661     exifTagTypes : {
31662         // byte, 8-bit unsigned int:
31663         1: {
31664             getValue: function (dataView, dataOffset) {
31665                 return dataView.getUint8(dataOffset);
31666             },
31667             size: 1
31668         },
31669         // ascii, 8-bit byte:
31670         2: {
31671             getValue: function (dataView, dataOffset) {
31672                 return String.fromCharCode(dataView.getUint8(dataOffset));
31673             },
31674             size: 1,
31675             ascii: true
31676         },
31677         // short, 16 bit int:
31678         3: {
31679             getValue: function (dataView, dataOffset, littleEndian) {
31680                 return dataView.getUint16(dataOffset, littleEndian);
31681             },
31682             size: 2
31683         },
31684         // long, 32 bit int:
31685         4: {
31686             getValue: function (dataView, dataOffset, littleEndian) {
31687                 return dataView.getUint32(dataOffset, littleEndian);
31688             },
31689             size: 4
31690         },
31691         // rational = two long values, first is numerator, second is denominator:
31692         5: {
31693             getValue: function (dataView, dataOffset, littleEndian) {
31694                 return dataView.getUint32(dataOffset, littleEndian) /
31695                     dataView.getUint32(dataOffset + 4, littleEndian);
31696             },
31697             size: 8
31698         },
31699         // slong, 32 bit signed int:
31700         9: {
31701             getValue: function (dataView, dataOffset, littleEndian) {
31702                 return dataView.getInt32(dataOffset, littleEndian);
31703             },
31704             size: 4
31705         },
31706         // srational, two slongs, first is numerator, second is denominator:
31707         10: {
31708             getValue: function (dataView, dataOffset, littleEndian) {
31709                 return dataView.getInt32(dataOffset, littleEndian) /
31710                     dataView.getInt32(dataOffset + 4, littleEndian);
31711             },
31712             size: 8
31713         }
31714     },
31715     
31716     footer : {
31717         STANDARD : [
31718             {
31719                 tag : 'div',
31720                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31721                 action : 'rotate-left',
31722                 cn : [
31723                     {
31724                         tag : 'button',
31725                         cls : 'btn btn-default',
31726                         html : '<i class="fa fa-undo"></i>'
31727                     }
31728                 ]
31729             },
31730             {
31731                 tag : 'div',
31732                 cls : 'btn-group roo-upload-cropbox-picture',
31733                 action : 'picture',
31734                 cn : [
31735                     {
31736                         tag : 'button',
31737                         cls : 'btn btn-default',
31738                         html : '<i class="fa fa-picture-o"></i>'
31739                     }
31740                 ]
31741             },
31742             {
31743                 tag : 'div',
31744                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31745                 action : 'rotate-right',
31746                 cn : [
31747                     {
31748                         tag : 'button',
31749                         cls : 'btn btn-default',
31750                         html : '<i class="fa fa-repeat"></i>'
31751                     }
31752                 ]
31753             }
31754         ],
31755         DOCUMENT : [
31756             {
31757                 tag : 'div',
31758                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31759                 action : 'rotate-left',
31760                 cn : [
31761                     {
31762                         tag : 'button',
31763                         cls : 'btn btn-default',
31764                         html : '<i class="fa fa-undo"></i>'
31765                     }
31766                 ]
31767             },
31768             {
31769                 tag : 'div',
31770                 cls : 'btn-group roo-upload-cropbox-download',
31771                 action : 'download',
31772                 cn : [
31773                     {
31774                         tag : 'button',
31775                         cls : 'btn btn-default',
31776                         html : '<i class="fa fa-download"></i>'
31777                     }
31778                 ]
31779             },
31780             {
31781                 tag : 'div',
31782                 cls : 'btn-group roo-upload-cropbox-crop',
31783                 action : 'crop',
31784                 cn : [
31785                     {
31786                         tag : 'button',
31787                         cls : 'btn btn-default',
31788                         html : '<i class="fa fa-crop"></i>'
31789                     }
31790                 ]
31791             },
31792             {
31793                 tag : 'div',
31794                 cls : 'btn-group roo-upload-cropbox-trash',
31795                 action : 'trash',
31796                 cn : [
31797                     {
31798                         tag : 'button',
31799                         cls : 'btn btn-default',
31800                         html : '<i class="fa fa-trash"></i>'
31801                     }
31802                 ]
31803             },
31804             {
31805                 tag : 'div',
31806                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31807                 action : 'rotate-right',
31808                 cn : [
31809                     {
31810                         tag : 'button',
31811                         cls : 'btn btn-default',
31812                         html : '<i class="fa fa-repeat"></i>'
31813                     }
31814                 ]
31815             }
31816         ],
31817         ROTATOR : [
31818             {
31819                 tag : 'div',
31820                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31821                 action : 'rotate-left',
31822                 cn : [
31823                     {
31824                         tag : 'button',
31825                         cls : 'btn btn-default',
31826                         html : '<i class="fa fa-undo"></i>'
31827                     }
31828                 ]
31829             },
31830             {
31831                 tag : 'div',
31832                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31833                 action : 'rotate-right',
31834                 cn : [
31835                     {
31836                         tag : 'button',
31837                         cls : 'btn btn-default',
31838                         html : '<i class="fa fa-repeat"></i>'
31839                     }
31840                 ]
31841             }
31842         ]
31843     }
31844 });
31845
31846 /*
31847 * Licence: LGPL
31848 */
31849
31850 /**
31851  * @class Roo.bootstrap.DocumentManager
31852  * @extends Roo.bootstrap.Component
31853  * Bootstrap DocumentManager class
31854  * @cfg {String} paramName default 'imageUpload'
31855  * @cfg {String} toolTipName default 'filename'
31856  * @cfg {String} method default POST
31857  * @cfg {String} url action url
31858  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31859  * @cfg {Boolean} multiple multiple upload default true
31860  * @cfg {Number} thumbSize default 300
31861  * @cfg {String} fieldLabel
31862  * @cfg {Number} labelWidth default 4
31863  * @cfg {String} labelAlign (left|top) default left
31864  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31865 * @cfg {Number} labellg set the width of label (1-12)
31866  * @cfg {Number} labelmd set the width of label (1-12)
31867  * @cfg {Number} labelsm set the width of label (1-12)
31868  * @cfg {Number} labelxs set the width of label (1-12)
31869  * 
31870  * @constructor
31871  * Create a new DocumentManager
31872  * @param {Object} config The config object
31873  */
31874
31875 Roo.bootstrap.DocumentManager = function(config){
31876     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31877     
31878     this.files = [];
31879     this.delegates = [];
31880     
31881     this.addEvents({
31882         /**
31883          * @event initial
31884          * Fire when initial the DocumentManager
31885          * @param {Roo.bootstrap.DocumentManager} this
31886          */
31887         "initial" : true,
31888         /**
31889          * @event inspect
31890          * inspect selected file
31891          * @param {Roo.bootstrap.DocumentManager} this
31892          * @param {File} file
31893          */
31894         "inspect" : true,
31895         /**
31896          * @event exception
31897          * Fire when xhr load exception
31898          * @param {Roo.bootstrap.DocumentManager} this
31899          * @param {XMLHttpRequest} xhr
31900          */
31901         "exception" : true,
31902         /**
31903          * @event afterupload
31904          * Fire when xhr load exception
31905          * @param {Roo.bootstrap.DocumentManager} this
31906          * @param {XMLHttpRequest} xhr
31907          */
31908         "afterupload" : true,
31909         /**
31910          * @event prepare
31911          * prepare the form data
31912          * @param {Roo.bootstrap.DocumentManager} this
31913          * @param {Object} formData
31914          */
31915         "prepare" : true,
31916         /**
31917          * @event remove
31918          * Fire when remove the file
31919          * @param {Roo.bootstrap.DocumentManager} this
31920          * @param {Object} file
31921          */
31922         "remove" : true,
31923         /**
31924          * @event refresh
31925          * Fire after refresh the file
31926          * @param {Roo.bootstrap.DocumentManager} this
31927          */
31928         "refresh" : true,
31929         /**
31930          * @event click
31931          * Fire after click the image
31932          * @param {Roo.bootstrap.DocumentManager} this
31933          * @param {Object} file
31934          */
31935         "click" : true,
31936         /**
31937          * @event edit
31938          * Fire when upload a image and editable set to true
31939          * @param {Roo.bootstrap.DocumentManager} this
31940          * @param {Object} file
31941          */
31942         "edit" : true,
31943         /**
31944          * @event beforeselectfile
31945          * Fire before select file
31946          * @param {Roo.bootstrap.DocumentManager} this
31947          */
31948         "beforeselectfile" : true,
31949         /**
31950          * @event process
31951          * Fire before process file
31952          * @param {Roo.bootstrap.DocumentManager} this
31953          * @param {Object} file
31954          */
31955         "process" : true,
31956         /**
31957          * @event previewrendered
31958          * Fire when preview rendered
31959          * @param {Roo.bootstrap.DocumentManager} this
31960          * @param {Object} file
31961          */
31962         "previewrendered" : true,
31963         /**
31964          */
31965         "previewResize" : true
31966         
31967     });
31968 };
31969
31970 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31971     
31972     boxes : 0,
31973     inputName : '',
31974     thumbSize : 300,
31975     multiple : true,
31976     files : false,
31977     method : 'POST',
31978     url : '',
31979     paramName : 'imageUpload',
31980     toolTipName : 'filename',
31981     fieldLabel : '',
31982     labelWidth : 4,
31983     labelAlign : 'left',
31984     editable : true,
31985     delegates : false,
31986     xhr : false, 
31987     
31988     labellg : 0,
31989     labelmd : 0,
31990     labelsm : 0,
31991     labelxs : 0,
31992     
31993     getAutoCreate : function()
31994     {   
31995         var managerWidget = {
31996             tag : 'div',
31997             cls : 'roo-document-manager',
31998             cn : [
31999                 {
32000                     tag : 'input',
32001                     cls : 'roo-document-manager-selector',
32002                     type : 'file'
32003                 },
32004                 {
32005                     tag : 'div',
32006                     cls : 'roo-document-manager-uploader',
32007                     cn : [
32008                         {
32009                             tag : 'div',
32010                             cls : 'roo-document-manager-upload-btn',
32011                             html : '<i class="fa fa-plus"></i>'
32012                         }
32013                     ]
32014                     
32015                 }
32016             ]
32017         };
32018         
32019         var content = [
32020             {
32021                 tag : 'div',
32022                 cls : 'column col-md-12',
32023                 cn : managerWidget
32024             }
32025         ];
32026         
32027         if(this.fieldLabel.length){
32028             
32029             content = [
32030                 {
32031                     tag : 'div',
32032                     cls : 'column col-md-12',
32033                     html : this.fieldLabel
32034                 },
32035                 {
32036                     tag : 'div',
32037                     cls : 'column col-md-12',
32038                     cn : managerWidget
32039                 }
32040             ];
32041
32042             if(this.labelAlign == 'left'){
32043                 content = [
32044                     {
32045                         tag : 'div',
32046                         cls : 'column',
32047                         html : this.fieldLabel
32048                     },
32049                     {
32050                         tag : 'div',
32051                         cls : 'column',
32052                         cn : managerWidget
32053                     }
32054                 ];
32055                 
32056                 if(this.labelWidth > 12){
32057                     content[0].style = "width: " + this.labelWidth + 'px';
32058                 }
32059
32060                 if(this.labelWidth < 13 && this.labelmd == 0){
32061                     this.labelmd = this.labelWidth;
32062                 }
32063
32064                 if(this.labellg > 0){
32065                     content[0].cls += ' col-lg-' + this.labellg;
32066                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32067                 }
32068
32069                 if(this.labelmd > 0){
32070                     content[0].cls += ' col-md-' + this.labelmd;
32071                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32072                 }
32073
32074                 if(this.labelsm > 0){
32075                     content[0].cls += ' col-sm-' + this.labelsm;
32076                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32077                 }
32078
32079                 if(this.labelxs > 0){
32080                     content[0].cls += ' col-xs-' + this.labelxs;
32081                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32082                 }
32083                 
32084             }
32085         }
32086         
32087         var cfg = {
32088             tag : 'div',
32089             cls : 'row clearfix',
32090             cn : content
32091         };
32092         
32093         return cfg;
32094         
32095     },
32096     
32097     initEvents : function()
32098     {
32099         this.managerEl = this.el.select('.roo-document-manager', true).first();
32100         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32101         
32102         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32103         this.selectorEl.hide();
32104         
32105         if(this.multiple){
32106             this.selectorEl.attr('multiple', 'multiple');
32107         }
32108         
32109         this.selectorEl.on('change', this.onFileSelected, this);
32110         
32111         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32112         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32113         
32114         this.uploader.on('click', this.onUploaderClick, this);
32115         
32116         this.renderProgressDialog();
32117         
32118         var _this = this;
32119         
32120         window.addEventListener("resize", function() { _this.refresh(); } );
32121         
32122         this.fireEvent('initial', this);
32123     },
32124     
32125     renderProgressDialog : function()
32126     {
32127         var _this = this;
32128         
32129         this.progressDialog = new Roo.bootstrap.Modal({
32130             cls : 'roo-document-manager-progress-dialog',
32131             allow_close : false,
32132             animate : false,
32133             title : '',
32134             buttons : [
32135                 {
32136                     name  :'cancel',
32137                     weight : 'danger',
32138                     html : 'Cancel'
32139                 }
32140             ], 
32141             listeners : { 
32142                 btnclick : function() {
32143                     _this.uploadCancel();
32144                     this.hide();
32145                 }
32146             }
32147         });
32148          
32149         this.progressDialog.render(Roo.get(document.body));
32150          
32151         this.progress = new Roo.bootstrap.Progress({
32152             cls : 'roo-document-manager-progress',
32153             active : true,
32154             striped : true
32155         });
32156         
32157         this.progress.render(this.progressDialog.getChildContainer());
32158         
32159         this.progressBar = new Roo.bootstrap.ProgressBar({
32160             cls : 'roo-document-manager-progress-bar',
32161             aria_valuenow : 0,
32162             aria_valuemin : 0,
32163             aria_valuemax : 12,
32164             panel : 'success'
32165         });
32166         
32167         this.progressBar.render(this.progress.getChildContainer());
32168     },
32169     
32170     onUploaderClick : function(e)
32171     {
32172         e.preventDefault();
32173      
32174         if(this.fireEvent('beforeselectfile', this) != false){
32175             this.selectorEl.dom.click();
32176         }
32177         
32178     },
32179     
32180     onFileSelected : function(e)
32181     {
32182         e.preventDefault();
32183         
32184         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32185             return;
32186         }
32187         
32188         Roo.each(this.selectorEl.dom.files, function(file){
32189             if(this.fireEvent('inspect', this, file) != false){
32190                 this.files.push(file);
32191             }
32192         }, this);
32193         
32194         this.queue();
32195         
32196     },
32197     
32198     queue : function()
32199     {
32200         this.selectorEl.dom.value = '';
32201         
32202         if(!this.files || !this.files.length){
32203             return;
32204         }
32205         
32206         if(this.boxes > 0 && this.files.length > this.boxes){
32207             this.files = this.files.slice(0, this.boxes);
32208         }
32209         
32210         this.uploader.show();
32211         
32212         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32213             this.uploader.hide();
32214         }
32215         
32216         var _this = this;
32217         
32218         var files = [];
32219         
32220         var docs = [];
32221         
32222         Roo.each(this.files, function(file){
32223             
32224             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32225                 var f = this.renderPreview(file);
32226                 files.push(f);
32227                 return;
32228             }
32229             
32230             if(file.type.indexOf('image') != -1){
32231                 this.delegates.push(
32232                     (function(){
32233                         _this.process(file);
32234                     }).createDelegate(this)
32235                 );
32236         
32237                 return;
32238             }
32239             
32240             docs.push(
32241                 (function(){
32242                     _this.process(file);
32243                 }).createDelegate(this)
32244             );
32245             
32246         }, this);
32247         
32248         this.files = files;
32249         
32250         this.delegates = this.delegates.concat(docs);
32251         
32252         if(!this.delegates.length){
32253             this.refresh();
32254             return;
32255         }
32256         
32257         this.progressBar.aria_valuemax = this.delegates.length;
32258         
32259         this.arrange();
32260         
32261         return;
32262     },
32263     
32264     arrange : function()
32265     {
32266         if(!this.delegates.length){
32267             this.progressDialog.hide();
32268             this.refresh();
32269             return;
32270         }
32271         
32272         var delegate = this.delegates.shift();
32273         
32274         this.progressDialog.show();
32275         
32276         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32277         
32278         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32279         
32280         delegate();
32281     },
32282     
32283     refresh : function()
32284     {
32285         this.uploader.show();
32286         
32287         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32288             this.uploader.hide();
32289         }
32290         
32291         Roo.isTouch ? this.closable(false) : this.closable(true);
32292         
32293         this.fireEvent('refresh', this);
32294     },
32295     
32296     onRemove : function(e, el, o)
32297     {
32298         e.preventDefault();
32299         
32300         this.fireEvent('remove', this, o);
32301         
32302     },
32303     
32304     remove : function(o)
32305     {
32306         var files = [];
32307         
32308         Roo.each(this.files, function(file){
32309             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32310                 files.push(file);
32311                 return;
32312             }
32313
32314             o.target.remove();
32315
32316         }, this);
32317         
32318         this.files = files;
32319         
32320         this.refresh();
32321     },
32322     
32323     clear : function()
32324     {
32325         Roo.each(this.files, function(file){
32326             if(!file.target){
32327                 return;
32328             }
32329             
32330             file.target.remove();
32331
32332         }, this);
32333         
32334         this.files = [];
32335         
32336         this.refresh();
32337     },
32338     
32339     onClick : function(e, el, o)
32340     {
32341         e.preventDefault();
32342         
32343         this.fireEvent('click', this, o);
32344         
32345     },
32346     
32347     closable : function(closable)
32348     {
32349         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32350             
32351             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32352             
32353             if(closable){
32354                 el.show();
32355                 return;
32356             }
32357             
32358             el.hide();
32359             
32360         }, this);
32361     },
32362     
32363     xhrOnLoad : function(xhr)
32364     {
32365         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32366             el.remove();
32367         }, this);
32368         
32369         if (xhr.readyState !== 4) {
32370             this.arrange();
32371             this.fireEvent('exception', this, xhr);
32372             return;
32373         }
32374
32375         var response = Roo.decode(xhr.responseText);
32376         
32377         if(!response.success){
32378             this.arrange();
32379             this.fireEvent('exception', this, xhr);
32380             return;
32381         }
32382         
32383         var file = this.renderPreview(response.data);
32384         
32385         this.files.push(file);
32386         
32387         this.arrange();
32388         
32389         this.fireEvent('afterupload', this, xhr);
32390         
32391     },
32392     
32393     xhrOnError : function(xhr)
32394     {
32395         Roo.log('xhr on error');
32396         
32397         var response = Roo.decode(xhr.responseText);
32398           
32399         Roo.log(response);
32400         
32401         this.arrange();
32402     },
32403     
32404     process : function(file)
32405     {
32406         if(this.fireEvent('process', this, file) !== false){
32407             if(this.editable && file.type.indexOf('image') != -1){
32408                 this.fireEvent('edit', this, file);
32409                 return;
32410             }
32411
32412             this.uploadStart(file, false);
32413
32414             return;
32415         }
32416         
32417     },
32418     
32419     uploadStart : function(file, crop)
32420     {
32421         this.xhr = new XMLHttpRequest();
32422         
32423         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32424             this.arrange();
32425             return;
32426         }
32427         
32428         file.xhr = this.xhr;
32429             
32430         this.managerEl.createChild({
32431             tag : 'div',
32432             cls : 'roo-document-manager-loading',
32433             cn : [
32434                 {
32435                     tag : 'div',
32436                     tooltip : file.name,
32437                     cls : 'roo-document-manager-thumb',
32438                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32439                 }
32440             ]
32441
32442         });
32443
32444         this.xhr.open(this.method, this.url, true);
32445         
32446         var headers = {
32447             "Accept": "application/json",
32448             "Cache-Control": "no-cache",
32449             "X-Requested-With": "XMLHttpRequest"
32450         };
32451         
32452         for (var headerName in headers) {
32453             var headerValue = headers[headerName];
32454             if (headerValue) {
32455                 this.xhr.setRequestHeader(headerName, headerValue);
32456             }
32457         }
32458         
32459         var _this = this;
32460         
32461         this.xhr.onload = function()
32462         {
32463             _this.xhrOnLoad(_this.xhr);
32464         }
32465         
32466         this.xhr.onerror = function()
32467         {
32468             _this.xhrOnError(_this.xhr);
32469         }
32470         
32471         var formData = new FormData();
32472
32473         formData.append('returnHTML', 'NO');
32474         
32475         if(crop){
32476             formData.append('crop', crop);
32477         }
32478         
32479         formData.append(this.paramName, file, file.name);
32480         
32481         var options = {
32482             file : file, 
32483             manually : false
32484         };
32485         
32486         if(this.fireEvent('prepare', this, formData, options) != false){
32487             
32488             if(options.manually){
32489                 return;
32490             }
32491             
32492             this.xhr.send(formData);
32493             return;
32494         };
32495         
32496         this.uploadCancel();
32497     },
32498     
32499     uploadCancel : function()
32500     {
32501         if (this.xhr) {
32502             this.xhr.abort();
32503         }
32504         
32505         this.delegates = [];
32506         
32507         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32508             el.remove();
32509         }, this);
32510         
32511         this.arrange();
32512     },
32513     
32514     renderPreview : function(file)
32515     {
32516         if(typeof(file.target) != 'undefined' && file.target){
32517             return file;
32518         }
32519         
32520         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32521         
32522         var previewEl = this.managerEl.createChild({
32523             tag : 'div',
32524             cls : 'roo-document-manager-preview',
32525             cn : [
32526                 {
32527                     tag : 'div',
32528                     tooltip : file[this.toolTipName],
32529                     cls : 'roo-document-manager-thumb',
32530                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32531                 },
32532                 {
32533                     tag : 'button',
32534                     cls : 'close',
32535                     html : '<i class="fa fa-times-circle"></i>'
32536                 }
32537             ]
32538         });
32539
32540         var close = previewEl.select('button.close', true).first();
32541
32542         close.on('click', this.onRemove, this, file);
32543
32544         file.target = previewEl;
32545
32546         var image = previewEl.select('img', true).first();
32547         
32548         var _this = this;
32549         
32550         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32551         
32552         image.on('click', this.onClick, this, file);
32553         
32554         this.fireEvent('previewrendered', this, file);
32555         
32556         return file;
32557         
32558     },
32559     
32560     onPreviewLoad : function(file, image)
32561     {
32562         if(typeof(file.target) == 'undefined' || !file.target){
32563             return;
32564         }
32565         
32566         var width = image.dom.naturalWidth || image.dom.width;
32567         var height = image.dom.naturalHeight || image.dom.height;
32568         
32569         if(!this.previewResize) {
32570             return;
32571         }
32572         
32573         if(width > height){
32574             file.target.addClass('wide');
32575             return;
32576         }
32577         
32578         file.target.addClass('tall');
32579         return;
32580         
32581     },
32582     
32583     uploadFromSource : function(file, crop)
32584     {
32585         this.xhr = new XMLHttpRequest();
32586         
32587         this.managerEl.createChild({
32588             tag : 'div',
32589             cls : 'roo-document-manager-loading',
32590             cn : [
32591                 {
32592                     tag : 'div',
32593                     tooltip : file.name,
32594                     cls : 'roo-document-manager-thumb',
32595                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32596                 }
32597             ]
32598
32599         });
32600
32601         this.xhr.open(this.method, this.url, true);
32602         
32603         var headers = {
32604             "Accept": "application/json",
32605             "Cache-Control": "no-cache",
32606             "X-Requested-With": "XMLHttpRequest"
32607         };
32608         
32609         for (var headerName in headers) {
32610             var headerValue = headers[headerName];
32611             if (headerValue) {
32612                 this.xhr.setRequestHeader(headerName, headerValue);
32613             }
32614         }
32615         
32616         var _this = this;
32617         
32618         this.xhr.onload = function()
32619         {
32620             _this.xhrOnLoad(_this.xhr);
32621         }
32622         
32623         this.xhr.onerror = function()
32624         {
32625             _this.xhrOnError(_this.xhr);
32626         }
32627         
32628         var formData = new FormData();
32629
32630         formData.append('returnHTML', 'NO');
32631         
32632         formData.append('crop', crop);
32633         
32634         if(typeof(file.filename) != 'undefined'){
32635             formData.append('filename', file.filename);
32636         }
32637         
32638         if(typeof(file.mimetype) != 'undefined'){
32639             formData.append('mimetype', file.mimetype);
32640         }
32641         
32642         Roo.log(formData);
32643         
32644         if(this.fireEvent('prepare', this, formData) != false){
32645             this.xhr.send(formData);
32646         };
32647     }
32648 });
32649
32650 /*
32651 * Licence: LGPL
32652 */
32653
32654 /**
32655  * @class Roo.bootstrap.DocumentViewer
32656  * @extends Roo.bootstrap.Component
32657  * Bootstrap DocumentViewer class
32658  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32659  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32660  * 
32661  * @constructor
32662  * Create a new DocumentViewer
32663  * @param {Object} config The config object
32664  */
32665
32666 Roo.bootstrap.DocumentViewer = function(config){
32667     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32668     
32669     this.addEvents({
32670         /**
32671          * @event initial
32672          * Fire after initEvent
32673          * @param {Roo.bootstrap.DocumentViewer} this
32674          */
32675         "initial" : true,
32676         /**
32677          * @event click
32678          * Fire after click
32679          * @param {Roo.bootstrap.DocumentViewer} this
32680          */
32681         "click" : true,
32682         /**
32683          * @event download
32684          * Fire after download button
32685          * @param {Roo.bootstrap.DocumentViewer} this
32686          */
32687         "download" : true,
32688         /**
32689          * @event trash
32690          * Fire after trash button
32691          * @param {Roo.bootstrap.DocumentViewer} this
32692          */
32693         "trash" : true
32694         
32695     });
32696 };
32697
32698 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32699     
32700     showDownload : true,
32701     
32702     showTrash : true,
32703     
32704     getAutoCreate : function()
32705     {
32706         var cfg = {
32707             tag : 'div',
32708             cls : 'roo-document-viewer',
32709             cn : [
32710                 {
32711                     tag : 'div',
32712                     cls : 'roo-document-viewer-body',
32713                     cn : [
32714                         {
32715                             tag : 'div',
32716                             cls : 'roo-document-viewer-thumb',
32717                             cn : [
32718                                 {
32719                                     tag : 'img',
32720                                     cls : 'roo-document-viewer-image'
32721                                 }
32722                             ]
32723                         }
32724                     ]
32725                 },
32726                 {
32727                     tag : 'div',
32728                     cls : 'roo-document-viewer-footer',
32729                     cn : {
32730                         tag : 'div',
32731                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32732                         cn : [
32733                             {
32734                                 tag : 'div',
32735                                 cls : 'btn-group roo-document-viewer-download',
32736                                 cn : [
32737                                     {
32738                                         tag : 'button',
32739                                         cls : 'btn btn-default',
32740                                         html : '<i class="fa fa-download"></i>'
32741                                     }
32742                                 ]
32743                             },
32744                             {
32745                                 tag : 'div',
32746                                 cls : 'btn-group roo-document-viewer-trash',
32747                                 cn : [
32748                                     {
32749                                         tag : 'button',
32750                                         cls : 'btn btn-default',
32751                                         html : '<i class="fa fa-trash"></i>'
32752                                     }
32753                                 ]
32754                             }
32755                         ]
32756                     }
32757                 }
32758             ]
32759         };
32760         
32761         return cfg;
32762     },
32763     
32764     initEvents : function()
32765     {
32766         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32767         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32768         
32769         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32770         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32771         
32772         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32773         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32774         
32775         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32776         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32777         
32778         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32779         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32780         
32781         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32782         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32783         
32784         this.bodyEl.on('click', this.onClick, this);
32785         this.downloadBtn.on('click', this.onDownload, this);
32786         this.trashBtn.on('click', this.onTrash, this);
32787         
32788         this.downloadBtn.hide();
32789         this.trashBtn.hide();
32790         
32791         if(this.showDownload){
32792             this.downloadBtn.show();
32793         }
32794         
32795         if(this.showTrash){
32796             this.trashBtn.show();
32797         }
32798         
32799         if(!this.showDownload && !this.showTrash) {
32800             this.footerEl.hide();
32801         }
32802         
32803     },
32804     
32805     initial : function()
32806     {
32807         this.fireEvent('initial', this);
32808         
32809     },
32810     
32811     onClick : function(e)
32812     {
32813         e.preventDefault();
32814         
32815         this.fireEvent('click', this);
32816     },
32817     
32818     onDownload : function(e)
32819     {
32820         e.preventDefault();
32821         
32822         this.fireEvent('download', this);
32823     },
32824     
32825     onTrash : function(e)
32826     {
32827         e.preventDefault();
32828         
32829         this.fireEvent('trash', this);
32830     }
32831     
32832 });
32833 /*
32834  * - LGPL
32835  *
32836  * nav progress bar
32837  * 
32838  */
32839
32840 /**
32841  * @class Roo.bootstrap.NavProgressBar
32842  * @extends Roo.bootstrap.Component
32843  * Bootstrap NavProgressBar class
32844  * 
32845  * @constructor
32846  * Create a new nav progress bar
32847  * @param {Object} config The config object
32848  */
32849
32850 Roo.bootstrap.NavProgressBar = function(config){
32851     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32852
32853     this.bullets = this.bullets || [];
32854    
32855 //    Roo.bootstrap.NavProgressBar.register(this);
32856      this.addEvents({
32857         /**
32858              * @event changed
32859              * Fires when the active item changes
32860              * @param {Roo.bootstrap.NavProgressBar} this
32861              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32862              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32863          */
32864         'changed': true
32865      });
32866     
32867 };
32868
32869 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32870     
32871     bullets : [],
32872     barItems : [],
32873     
32874     getAutoCreate : function()
32875     {
32876         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32877         
32878         cfg = {
32879             tag : 'div',
32880             cls : 'roo-navigation-bar-group',
32881             cn : [
32882                 {
32883                     tag : 'div',
32884                     cls : 'roo-navigation-top-bar'
32885                 },
32886                 {
32887                     tag : 'div',
32888                     cls : 'roo-navigation-bullets-bar',
32889                     cn : [
32890                         {
32891                             tag : 'ul',
32892                             cls : 'roo-navigation-bar'
32893                         }
32894                     ]
32895                 },
32896                 
32897                 {
32898                     tag : 'div',
32899                     cls : 'roo-navigation-bottom-bar'
32900                 }
32901             ]
32902             
32903         };
32904         
32905         return cfg;
32906         
32907     },
32908     
32909     initEvents: function() 
32910     {
32911         
32912     },
32913     
32914     onRender : function(ct, position) 
32915     {
32916         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32917         
32918         if(this.bullets.length){
32919             Roo.each(this.bullets, function(b){
32920                this.addItem(b);
32921             }, this);
32922         }
32923         
32924         this.format();
32925         
32926     },
32927     
32928     addItem : function(cfg)
32929     {
32930         var item = new Roo.bootstrap.NavProgressItem(cfg);
32931         
32932         item.parentId = this.id;
32933         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32934         
32935         if(cfg.html){
32936             var top = new Roo.bootstrap.Element({
32937                 tag : 'div',
32938                 cls : 'roo-navigation-bar-text'
32939             });
32940             
32941             var bottom = new Roo.bootstrap.Element({
32942                 tag : 'div',
32943                 cls : 'roo-navigation-bar-text'
32944             });
32945             
32946             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32947             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32948             
32949             var topText = new Roo.bootstrap.Element({
32950                 tag : 'span',
32951                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32952             });
32953             
32954             var bottomText = new Roo.bootstrap.Element({
32955                 tag : 'span',
32956                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32957             });
32958             
32959             topText.onRender(top.el, null);
32960             bottomText.onRender(bottom.el, null);
32961             
32962             item.topEl = top;
32963             item.bottomEl = bottom;
32964         }
32965         
32966         this.barItems.push(item);
32967         
32968         return item;
32969     },
32970     
32971     getActive : function()
32972     {
32973         var active = false;
32974         
32975         Roo.each(this.barItems, function(v){
32976             
32977             if (!v.isActive()) {
32978                 return;
32979             }
32980             
32981             active = v;
32982             return false;
32983             
32984         });
32985         
32986         return active;
32987     },
32988     
32989     setActiveItem : function(item)
32990     {
32991         var prev = false;
32992         
32993         Roo.each(this.barItems, function(v){
32994             if (v.rid == item.rid) {
32995                 return ;
32996             }
32997             
32998             if (v.isActive()) {
32999                 v.setActive(false);
33000                 prev = v;
33001             }
33002         });
33003
33004         item.setActive(true);
33005         
33006         this.fireEvent('changed', this, item, prev);
33007     },
33008     
33009     getBarItem: function(rid)
33010     {
33011         var ret = false;
33012         
33013         Roo.each(this.barItems, function(e) {
33014             if (e.rid != rid) {
33015                 return;
33016             }
33017             
33018             ret =  e;
33019             return false;
33020         });
33021         
33022         return ret;
33023     },
33024     
33025     indexOfItem : function(item)
33026     {
33027         var index = false;
33028         
33029         Roo.each(this.barItems, function(v, i){
33030             
33031             if (v.rid != item.rid) {
33032                 return;
33033             }
33034             
33035             index = i;
33036             return false
33037         });
33038         
33039         return index;
33040     },
33041     
33042     setActiveNext : function()
33043     {
33044         var i = this.indexOfItem(this.getActive());
33045         
33046         if (i > this.barItems.length) {
33047             return;
33048         }
33049         
33050         this.setActiveItem(this.barItems[i+1]);
33051     },
33052     
33053     setActivePrev : function()
33054     {
33055         var i = this.indexOfItem(this.getActive());
33056         
33057         if (i  < 1) {
33058             return;
33059         }
33060         
33061         this.setActiveItem(this.barItems[i-1]);
33062     },
33063     
33064     format : function()
33065     {
33066         if(!this.barItems.length){
33067             return;
33068         }
33069      
33070         var width = 100 / this.barItems.length;
33071         
33072         Roo.each(this.barItems, function(i){
33073             i.el.setStyle('width', width + '%');
33074             i.topEl.el.setStyle('width', width + '%');
33075             i.bottomEl.el.setStyle('width', width + '%');
33076         }, this);
33077         
33078     }
33079     
33080 });
33081 /*
33082  * - LGPL
33083  *
33084  * Nav Progress Item
33085  * 
33086  */
33087
33088 /**
33089  * @class Roo.bootstrap.NavProgressItem
33090  * @extends Roo.bootstrap.Component
33091  * Bootstrap NavProgressItem class
33092  * @cfg {String} rid the reference id
33093  * @cfg {Boolean} active (true|false) Is item active default false
33094  * @cfg {Boolean} disabled (true|false) Is item active default false
33095  * @cfg {String} html
33096  * @cfg {String} position (top|bottom) text position default bottom
33097  * @cfg {String} icon show icon instead of number
33098  * 
33099  * @constructor
33100  * Create a new NavProgressItem
33101  * @param {Object} config The config object
33102  */
33103 Roo.bootstrap.NavProgressItem = function(config){
33104     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33105     this.addEvents({
33106         // raw events
33107         /**
33108          * @event click
33109          * The raw click event for the entire grid.
33110          * @param {Roo.bootstrap.NavProgressItem} this
33111          * @param {Roo.EventObject} e
33112          */
33113         "click" : true
33114     });
33115    
33116 };
33117
33118 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33119     
33120     rid : '',
33121     active : false,
33122     disabled : false,
33123     html : '',
33124     position : 'bottom',
33125     icon : false,
33126     
33127     getAutoCreate : function()
33128     {
33129         var iconCls = 'roo-navigation-bar-item-icon';
33130         
33131         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33132         
33133         var cfg = {
33134             tag: 'li',
33135             cls: 'roo-navigation-bar-item',
33136             cn : [
33137                 {
33138                     tag : 'i',
33139                     cls : iconCls
33140                 }
33141             ]
33142         };
33143         
33144         if(this.active){
33145             cfg.cls += ' active';
33146         }
33147         if(this.disabled){
33148             cfg.cls += ' disabled';
33149         }
33150         
33151         return cfg;
33152     },
33153     
33154     disable : function()
33155     {
33156         this.setDisabled(true);
33157     },
33158     
33159     enable : function()
33160     {
33161         this.setDisabled(false);
33162     },
33163     
33164     initEvents: function() 
33165     {
33166         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33167         
33168         this.iconEl.on('click', this.onClick, this);
33169     },
33170     
33171     onClick : function(e)
33172     {
33173         e.preventDefault();
33174         
33175         if(this.disabled){
33176             return;
33177         }
33178         
33179         if(this.fireEvent('click', this, e) === false){
33180             return;
33181         };
33182         
33183         this.parent().setActiveItem(this);
33184     },
33185     
33186     isActive: function () 
33187     {
33188         return this.active;
33189     },
33190     
33191     setActive : function(state)
33192     {
33193         if(this.active == state){
33194             return;
33195         }
33196         
33197         this.active = state;
33198         
33199         if (state) {
33200             this.el.addClass('active');
33201             return;
33202         }
33203         
33204         this.el.removeClass('active');
33205         
33206         return;
33207     },
33208     
33209     setDisabled : function(state)
33210     {
33211         if(this.disabled == state){
33212             return;
33213         }
33214         
33215         this.disabled = state;
33216         
33217         if (state) {
33218             this.el.addClass('disabled');
33219             return;
33220         }
33221         
33222         this.el.removeClass('disabled');
33223     },
33224     
33225     tooltipEl : function()
33226     {
33227         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33228     }
33229 });
33230  
33231
33232  /*
33233  * - LGPL
33234  *
33235  * FieldLabel
33236  * 
33237  */
33238
33239 /**
33240  * @class Roo.bootstrap.FieldLabel
33241  * @extends Roo.bootstrap.Component
33242  * Bootstrap FieldLabel class
33243  * @cfg {String} html contents of the element
33244  * @cfg {String} tag tag of the element default label
33245  * @cfg {String} cls class of the element
33246  * @cfg {String} target label target 
33247  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33248  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33249  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33250  * @cfg {String} iconTooltip default "This field is required"
33251  * @cfg {String} indicatorpos (left|right) default left
33252  * 
33253  * @constructor
33254  * Create a new FieldLabel
33255  * @param {Object} config The config object
33256  */
33257
33258 Roo.bootstrap.FieldLabel = function(config){
33259     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33260     
33261     this.addEvents({
33262             /**
33263              * @event invalid
33264              * Fires after the field has been marked as invalid.
33265              * @param {Roo.form.FieldLabel} this
33266              * @param {String} msg The validation message
33267              */
33268             invalid : true,
33269             /**
33270              * @event valid
33271              * Fires after the field has been validated with no errors.
33272              * @param {Roo.form.FieldLabel} this
33273              */
33274             valid : true
33275         });
33276 };
33277
33278 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33279     
33280     tag: 'label',
33281     cls: '',
33282     html: '',
33283     target: '',
33284     allowBlank : true,
33285     invalidClass : 'has-warning',
33286     validClass : 'has-success',
33287     iconTooltip : 'This field is required',
33288     indicatorpos : 'left',
33289     
33290     getAutoCreate : function(){
33291         
33292         var cls = "";
33293         if (!this.allowBlank) {
33294             cls  = "visible";
33295         }
33296         
33297         var cfg = {
33298             tag : this.tag,
33299             cls : 'roo-bootstrap-field-label ' + this.cls,
33300             for : this.target,
33301             cn : [
33302                 {
33303                     tag : 'i',
33304                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33305                     tooltip : this.iconTooltip
33306                 },
33307                 {
33308                     tag : 'span',
33309                     html : this.html
33310                 }
33311             ] 
33312         };
33313         
33314         if(this.indicatorpos == 'right'){
33315             var cfg = {
33316                 tag : this.tag,
33317                 cls : 'roo-bootstrap-field-label ' + this.cls,
33318                 for : this.target,
33319                 cn : [
33320                     {
33321                         tag : 'span',
33322                         html : this.html
33323                     },
33324                     {
33325                         tag : 'i',
33326                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33327                         tooltip : this.iconTooltip
33328                     }
33329                 ] 
33330             };
33331         }
33332         
33333         return cfg;
33334     },
33335     
33336     initEvents: function() 
33337     {
33338         Roo.bootstrap.Element.superclass.initEvents.call(this);
33339         
33340         this.indicator = this.indicatorEl();
33341         
33342         if(this.indicator){
33343             this.indicator.removeClass('visible');
33344             this.indicator.addClass('invisible');
33345         }
33346         
33347         Roo.bootstrap.FieldLabel.register(this);
33348     },
33349     
33350     indicatorEl : function()
33351     {
33352         var indicator = this.el.select('i.roo-required-indicator',true).first();
33353         
33354         if(!indicator){
33355             return false;
33356         }
33357         
33358         return indicator;
33359         
33360     },
33361     
33362     /**
33363      * Mark this field as valid
33364      */
33365     markValid : function()
33366     {
33367         if(this.indicator){
33368             this.indicator.removeClass('visible');
33369             this.indicator.addClass('invisible');
33370         }
33371         if (Roo.bootstrap.version == 3) {
33372             this.el.removeClass(this.invalidClass);
33373             this.el.addClass(this.validClass);
33374         } else {
33375             this.el.removeClass('is-invalid');
33376             this.el.addClass('is-valid');
33377         }
33378         
33379         
33380         this.fireEvent('valid', this);
33381     },
33382     
33383     /**
33384      * Mark this field as invalid
33385      * @param {String} msg The validation message
33386      */
33387     markInvalid : function(msg)
33388     {
33389         if(this.indicator){
33390             this.indicator.removeClass('invisible');
33391             this.indicator.addClass('visible');
33392         }
33393           if (Roo.bootstrap.version == 3) {
33394             this.el.removeClass(this.validClass);
33395             this.el.addClass(this.invalidClass);
33396         } else {
33397             this.el.removeClass('is-valid');
33398             this.el.addClass('is-invalid');
33399         }
33400         
33401         
33402         this.fireEvent('invalid', this, msg);
33403     }
33404     
33405    
33406 });
33407
33408 Roo.apply(Roo.bootstrap.FieldLabel, {
33409     
33410     groups: {},
33411     
33412      /**
33413     * register a FieldLabel Group
33414     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33415     */
33416     register : function(label)
33417     {
33418         if(this.groups.hasOwnProperty(label.target)){
33419             return;
33420         }
33421      
33422         this.groups[label.target] = label;
33423         
33424     },
33425     /**
33426     * fetch a FieldLabel Group based on the target
33427     * @param {string} target
33428     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33429     */
33430     get: function(target) {
33431         if (typeof(this.groups[target]) == 'undefined') {
33432             return false;
33433         }
33434         
33435         return this.groups[target] ;
33436     }
33437 });
33438
33439  
33440
33441  /*
33442  * - LGPL
33443  *
33444  * page DateSplitField.
33445  * 
33446  */
33447
33448
33449 /**
33450  * @class Roo.bootstrap.DateSplitField
33451  * @extends Roo.bootstrap.Component
33452  * Bootstrap DateSplitField class
33453  * @cfg {string} fieldLabel - the label associated
33454  * @cfg {Number} labelWidth set the width of label (0-12)
33455  * @cfg {String} labelAlign (top|left)
33456  * @cfg {Boolean} dayAllowBlank (true|false) default false
33457  * @cfg {Boolean} monthAllowBlank (true|false) default false
33458  * @cfg {Boolean} yearAllowBlank (true|false) default false
33459  * @cfg {string} dayPlaceholder 
33460  * @cfg {string} monthPlaceholder
33461  * @cfg {string} yearPlaceholder
33462  * @cfg {string} dayFormat default 'd'
33463  * @cfg {string} monthFormat default 'm'
33464  * @cfg {string} yearFormat default 'Y'
33465  * @cfg {Number} labellg set the width of label (1-12)
33466  * @cfg {Number} labelmd set the width of label (1-12)
33467  * @cfg {Number} labelsm set the width of label (1-12)
33468  * @cfg {Number} labelxs set the width of label (1-12)
33469
33470  *     
33471  * @constructor
33472  * Create a new DateSplitField
33473  * @param {Object} config The config object
33474  */
33475
33476 Roo.bootstrap.DateSplitField = function(config){
33477     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33478     
33479     this.addEvents({
33480         // raw events
33481          /**
33482          * @event years
33483          * getting the data of years
33484          * @param {Roo.bootstrap.DateSplitField} this
33485          * @param {Object} years
33486          */
33487         "years" : true,
33488         /**
33489          * @event days
33490          * getting the data of days
33491          * @param {Roo.bootstrap.DateSplitField} this
33492          * @param {Object} days
33493          */
33494         "days" : true,
33495         /**
33496          * @event invalid
33497          * Fires after the field has been marked as invalid.
33498          * @param {Roo.form.Field} this
33499          * @param {String} msg The validation message
33500          */
33501         invalid : true,
33502        /**
33503          * @event valid
33504          * Fires after the field has been validated with no errors.
33505          * @param {Roo.form.Field} this
33506          */
33507         valid : true
33508     });
33509 };
33510
33511 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33512     
33513     fieldLabel : '',
33514     labelAlign : 'top',
33515     labelWidth : 3,
33516     dayAllowBlank : false,
33517     monthAllowBlank : false,
33518     yearAllowBlank : false,
33519     dayPlaceholder : '',
33520     monthPlaceholder : '',
33521     yearPlaceholder : '',
33522     dayFormat : 'd',
33523     monthFormat : 'm',
33524     yearFormat : 'Y',
33525     isFormField : true,
33526     labellg : 0,
33527     labelmd : 0,
33528     labelsm : 0,
33529     labelxs : 0,
33530     
33531     getAutoCreate : function()
33532     {
33533         var cfg = {
33534             tag : 'div',
33535             cls : 'row roo-date-split-field-group',
33536             cn : [
33537                 {
33538                     tag : 'input',
33539                     type : 'hidden',
33540                     cls : 'form-hidden-field roo-date-split-field-group-value',
33541                     name : this.name
33542                 }
33543             ]
33544         };
33545         
33546         var labelCls = 'col-md-12';
33547         var contentCls = 'col-md-4';
33548         
33549         if(this.fieldLabel){
33550             
33551             var label = {
33552                 tag : 'div',
33553                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33554                 cn : [
33555                     {
33556                         tag : 'label',
33557                         html : this.fieldLabel
33558                     }
33559                 ]
33560             };
33561             
33562             if(this.labelAlign == 'left'){
33563             
33564                 if(this.labelWidth > 12){
33565                     label.style = "width: " + this.labelWidth + 'px';
33566                 }
33567
33568                 if(this.labelWidth < 13 && this.labelmd == 0){
33569                     this.labelmd = this.labelWidth;
33570                 }
33571
33572                 if(this.labellg > 0){
33573                     labelCls = ' col-lg-' + this.labellg;
33574                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33575                 }
33576
33577                 if(this.labelmd > 0){
33578                     labelCls = ' col-md-' + this.labelmd;
33579                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33580                 }
33581
33582                 if(this.labelsm > 0){
33583                     labelCls = ' col-sm-' + this.labelsm;
33584                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33585                 }
33586
33587                 if(this.labelxs > 0){
33588                     labelCls = ' col-xs-' + this.labelxs;
33589                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33590                 }
33591             }
33592             
33593             label.cls += ' ' + labelCls;
33594             
33595             cfg.cn.push(label);
33596         }
33597         
33598         Roo.each(['day', 'month', 'year'], function(t){
33599             cfg.cn.push({
33600                 tag : 'div',
33601                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33602             });
33603         }, this);
33604         
33605         return cfg;
33606     },
33607     
33608     inputEl: function ()
33609     {
33610         return this.el.select('.roo-date-split-field-group-value', true).first();
33611     },
33612     
33613     onRender : function(ct, position) 
33614     {
33615         var _this = this;
33616         
33617         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33618         
33619         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33620         
33621         this.dayField = new Roo.bootstrap.ComboBox({
33622             allowBlank : this.dayAllowBlank,
33623             alwaysQuery : true,
33624             displayField : 'value',
33625             editable : false,
33626             fieldLabel : '',
33627             forceSelection : true,
33628             mode : 'local',
33629             placeholder : this.dayPlaceholder,
33630             selectOnFocus : true,
33631             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33632             triggerAction : 'all',
33633             typeAhead : true,
33634             valueField : 'value',
33635             store : new Roo.data.SimpleStore({
33636                 data : (function() {    
33637                     var days = [];
33638                     _this.fireEvent('days', _this, days);
33639                     return days;
33640                 })(),
33641                 fields : [ 'value' ]
33642             }),
33643             listeners : {
33644                 select : function (_self, record, index)
33645                 {
33646                     _this.setValue(_this.getValue());
33647                 }
33648             }
33649         });
33650
33651         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33652         
33653         this.monthField = new Roo.bootstrap.MonthField({
33654             after : '<i class=\"fa fa-calendar\"></i>',
33655             allowBlank : this.monthAllowBlank,
33656             placeholder : this.monthPlaceholder,
33657             readOnly : true,
33658             listeners : {
33659                 render : function (_self)
33660                 {
33661                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33662                         e.preventDefault();
33663                         _self.focus();
33664                     });
33665                 },
33666                 select : function (_self, oldvalue, newvalue)
33667                 {
33668                     _this.setValue(_this.getValue());
33669                 }
33670             }
33671         });
33672         
33673         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33674         
33675         this.yearField = new Roo.bootstrap.ComboBox({
33676             allowBlank : this.yearAllowBlank,
33677             alwaysQuery : true,
33678             displayField : 'value',
33679             editable : false,
33680             fieldLabel : '',
33681             forceSelection : true,
33682             mode : 'local',
33683             placeholder : this.yearPlaceholder,
33684             selectOnFocus : true,
33685             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33686             triggerAction : 'all',
33687             typeAhead : true,
33688             valueField : 'value',
33689             store : new Roo.data.SimpleStore({
33690                 data : (function() {
33691                     var years = [];
33692                     _this.fireEvent('years', _this, years);
33693                     return years;
33694                 })(),
33695                 fields : [ 'value' ]
33696             }),
33697             listeners : {
33698                 select : function (_self, record, index)
33699                 {
33700                     _this.setValue(_this.getValue());
33701                 }
33702             }
33703         });
33704
33705         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33706     },
33707     
33708     setValue : function(v, format)
33709     {
33710         this.inputEl.dom.value = v;
33711         
33712         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33713         
33714         var d = Date.parseDate(v, f);
33715         
33716         if(!d){
33717             this.validate();
33718             return;
33719         }
33720         
33721         this.setDay(d.format(this.dayFormat));
33722         this.setMonth(d.format(this.monthFormat));
33723         this.setYear(d.format(this.yearFormat));
33724         
33725         this.validate();
33726         
33727         return;
33728     },
33729     
33730     setDay : function(v)
33731     {
33732         this.dayField.setValue(v);
33733         this.inputEl.dom.value = this.getValue();
33734         this.validate();
33735         return;
33736     },
33737     
33738     setMonth : function(v)
33739     {
33740         this.monthField.setValue(v, true);
33741         this.inputEl.dom.value = this.getValue();
33742         this.validate();
33743         return;
33744     },
33745     
33746     setYear : function(v)
33747     {
33748         this.yearField.setValue(v);
33749         this.inputEl.dom.value = this.getValue();
33750         this.validate();
33751         return;
33752     },
33753     
33754     getDay : function()
33755     {
33756         return this.dayField.getValue();
33757     },
33758     
33759     getMonth : function()
33760     {
33761         return this.monthField.getValue();
33762     },
33763     
33764     getYear : function()
33765     {
33766         return this.yearField.getValue();
33767     },
33768     
33769     getValue : function()
33770     {
33771         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33772         
33773         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33774         
33775         return date;
33776     },
33777     
33778     reset : function()
33779     {
33780         this.setDay('');
33781         this.setMonth('');
33782         this.setYear('');
33783         this.inputEl.dom.value = '';
33784         this.validate();
33785         return;
33786     },
33787     
33788     validate : function()
33789     {
33790         var d = this.dayField.validate();
33791         var m = this.monthField.validate();
33792         var y = this.yearField.validate();
33793         
33794         var valid = true;
33795         
33796         if(
33797                 (!this.dayAllowBlank && !d) ||
33798                 (!this.monthAllowBlank && !m) ||
33799                 (!this.yearAllowBlank && !y)
33800         ){
33801             valid = false;
33802         }
33803         
33804         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33805             return valid;
33806         }
33807         
33808         if(valid){
33809             this.markValid();
33810             return valid;
33811         }
33812         
33813         this.markInvalid();
33814         
33815         return valid;
33816     },
33817     
33818     markValid : function()
33819     {
33820         
33821         var label = this.el.select('label', true).first();
33822         var icon = this.el.select('i.fa-star', true).first();
33823
33824         if(label && icon){
33825             icon.remove();
33826         }
33827         
33828         this.fireEvent('valid', this);
33829     },
33830     
33831      /**
33832      * Mark this field as invalid
33833      * @param {String} msg The validation message
33834      */
33835     markInvalid : function(msg)
33836     {
33837         
33838         var label = this.el.select('label', true).first();
33839         var icon = this.el.select('i.fa-star', true).first();
33840
33841         if(label && !icon){
33842             this.el.select('.roo-date-split-field-label', true).createChild({
33843                 tag : 'i',
33844                 cls : 'text-danger fa fa-lg fa-star',
33845                 tooltip : 'This field is required',
33846                 style : 'margin-right:5px;'
33847             }, label, true);
33848         }
33849         
33850         this.fireEvent('invalid', this, msg);
33851     },
33852     
33853     clearInvalid : function()
33854     {
33855         var label = this.el.select('label', true).first();
33856         var icon = this.el.select('i.fa-star', true).first();
33857
33858         if(label && icon){
33859             icon.remove();
33860         }
33861         
33862         this.fireEvent('valid', this);
33863     },
33864     
33865     getName: function()
33866     {
33867         return this.name;
33868     }
33869     
33870 });
33871
33872  /**
33873  *
33874  * This is based on 
33875  * http://masonry.desandro.com
33876  *
33877  * The idea is to render all the bricks based on vertical width...
33878  *
33879  * The original code extends 'outlayer' - we might need to use that....
33880  * 
33881  */
33882
33883
33884 /**
33885  * @class Roo.bootstrap.LayoutMasonry
33886  * @extends Roo.bootstrap.Component
33887  * Bootstrap Layout Masonry class
33888  * 
33889  * @constructor
33890  * Create a new Element
33891  * @param {Object} config The config object
33892  */
33893
33894 Roo.bootstrap.LayoutMasonry = function(config){
33895     
33896     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33897     
33898     this.bricks = [];
33899     
33900     Roo.bootstrap.LayoutMasonry.register(this);
33901     
33902     this.addEvents({
33903         // raw events
33904         /**
33905          * @event layout
33906          * Fire after layout the items
33907          * @param {Roo.bootstrap.LayoutMasonry} this
33908          * @param {Roo.EventObject} e
33909          */
33910         "layout" : true
33911     });
33912     
33913 };
33914
33915 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33916     
33917     /**
33918      * @cfg {Boolean} isLayoutInstant = no animation?
33919      */   
33920     isLayoutInstant : false, // needed?
33921    
33922     /**
33923      * @cfg {Number} boxWidth  width of the columns
33924      */   
33925     boxWidth : 450,
33926     
33927       /**
33928      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33929      */   
33930     boxHeight : 0,
33931     
33932     /**
33933      * @cfg {Number} padWidth padding below box..
33934      */   
33935     padWidth : 10, 
33936     
33937     /**
33938      * @cfg {Number} gutter gutter width..
33939      */   
33940     gutter : 10,
33941     
33942      /**
33943      * @cfg {Number} maxCols maximum number of columns
33944      */   
33945     
33946     maxCols: 0,
33947     
33948     /**
33949      * @cfg {Boolean} isAutoInitial defalut true
33950      */   
33951     isAutoInitial : true, 
33952     
33953     containerWidth: 0,
33954     
33955     /**
33956      * @cfg {Boolean} isHorizontal defalut false
33957      */   
33958     isHorizontal : false, 
33959
33960     currentSize : null,
33961     
33962     tag: 'div',
33963     
33964     cls: '',
33965     
33966     bricks: null, //CompositeElement
33967     
33968     cols : 1,
33969     
33970     _isLayoutInited : false,
33971     
33972 //    isAlternative : false, // only use for vertical layout...
33973     
33974     /**
33975      * @cfg {Number} alternativePadWidth padding below box..
33976      */   
33977     alternativePadWidth : 50,
33978     
33979     selectedBrick : [],
33980     
33981     getAutoCreate : function(){
33982         
33983         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33984         
33985         var cfg = {
33986             tag: this.tag,
33987             cls: 'blog-masonary-wrapper ' + this.cls,
33988             cn : {
33989                 cls : 'mas-boxes masonary'
33990             }
33991         };
33992         
33993         return cfg;
33994     },
33995     
33996     getChildContainer: function( )
33997     {
33998         if (this.boxesEl) {
33999             return this.boxesEl;
34000         }
34001         
34002         this.boxesEl = this.el.select('.mas-boxes').first();
34003         
34004         return this.boxesEl;
34005     },
34006     
34007     
34008     initEvents : function()
34009     {
34010         var _this = this;
34011         
34012         if(this.isAutoInitial){
34013             Roo.log('hook children rendered');
34014             this.on('childrenrendered', function() {
34015                 Roo.log('children rendered');
34016                 _this.initial();
34017             } ,this);
34018         }
34019     },
34020     
34021     initial : function()
34022     {
34023         this.selectedBrick = [];
34024         
34025         this.currentSize = this.el.getBox(true);
34026         
34027         Roo.EventManager.onWindowResize(this.resize, this); 
34028
34029         if(!this.isAutoInitial){
34030             this.layout();
34031             return;
34032         }
34033         
34034         this.layout();
34035         
34036         return;
34037         //this.layout.defer(500,this);
34038         
34039     },
34040     
34041     resize : function()
34042     {
34043         var cs = this.el.getBox(true);
34044         
34045         if (
34046                 this.currentSize.width == cs.width && 
34047                 this.currentSize.x == cs.x && 
34048                 this.currentSize.height == cs.height && 
34049                 this.currentSize.y == cs.y 
34050         ) {
34051             Roo.log("no change in with or X or Y");
34052             return;
34053         }
34054         
34055         this.currentSize = cs;
34056         
34057         this.layout();
34058         
34059     },
34060     
34061     layout : function()
34062     {   
34063         this._resetLayout();
34064         
34065         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34066         
34067         this.layoutItems( isInstant );
34068       
34069         this._isLayoutInited = true;
34070         
34071         this.fireEvent('layout', this);
34072         
34073     },
34074     
34075     _resetLayout : function()
34076     {
34077         if(this.isHorizontal){
34078             this.horizontalMeasureColumns();
34079             return;
34080         }
34081         
34082         this.verticalMeasureColumns();
34083         
34084     },
34085     
34086     verticalMeasureColumns : function()
34087     {
34088         this.getContainerWidth();
34089         
34090 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34091 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34092 //            return;
34093 //        }
34094         
34095         var boxWidth = this.boxWidth + this.padWidth;
34096         
34097         if(this.containerWidth < this.boxWidth){
34098             boxWidth = this.containerWidth
34099         }
34100         
34101         var containerWidth = this.containerWidth;
34102         
34103         var cols = Math.floor(containerWidth / boxWidth);
34104         
34105         this.cols = Math.max( cols, 1 );
34106         
34107         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34108         
34109         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34110         
34111         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34112         
34113         this.colWidth = boxWidth + avail - this.padWidth;
34114         
34115         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34116         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34117     },
34118     
34119     horizontalMeasureColumns : function()
34120     {
34121         this.getContainerWidth();
34122         
34123         var boxWidth = this.boxWidth;
34124         
34125         if(this.containerWidth < boxWidth){
34126             boxWidth = this.containerWidth;
34127         }
34128         
34129         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34130         
34131         this.el.setHeight(boxWidth);
34132         
34133     },
34134     
34135     getContainerWidth : function()
34136     {
34137         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34138     },
34139     
34140     layoutItems : function( isInstant )
34141     {
34142         Roo.log(this.bricks);
34143         
34144         var items = Roo.apply([], this.bricks);
34145         
34146         if(this.isHorizontal){
34147             this._horizontalLayoutItems( items , isInstant );
34148             return;
34149         }
34150         
34151 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34152 //            this._verticalAlternativeLayoutItems( items , isInstant );
34153 //            return;
34154 //        }
34155         
34156         this._verticalLayoutItems( items , isInstant );
34157         
34158     },
34159     
34160     _verticalLayoutItems : function ( items , isInstant)
34161     {
34162         if ( !items || !items.length ) {
34163             return;
34164         }
34165         
34166         var standard = [
34167             ['xs', 'xs', 'xs', 'tall'],
34168             ['xs', 'xs', 'tall'],
34169             ['xs', 'xs', 'sm'],
34170             ['xs', 'xs', 'xs'],
34171             ['xs', 'tall'],
34172             ['xs', 'sm'],
34173             ['xs', 'xs'],
34174             ['xs'],
34175             
34176             ['sm', 'xs', 'xs'],
34177             ['sm', 'xs'],
34178             ['sm'],
34179             
34180             ['tall', 'xs', 'xs', 'xs'],
34181             ['tall', 'xs', 'xs'],
34182             ['tall', 'xs'],
34183             ['tall']
34184             
34185         ];
34186         
34187         var queue = [];
34188         
34189         var boxes = [];
34190         
34191         var box = [];
34192         
34193         Roo.each(items, function(item, k){
34194             
34195             switch (item.size) {
34196                 // these layouts take up a full box,
34197                 case 'md' :
34198                 case 'md-left' :
34199                 case 'md-right' :
34200                 case 'wide' :
34201                     
34202                     if(box.length){
34203                         boxes.push(box);
34204                         box = [];
34205                     }
34206                     
34207                     boxes.push([item]);
34208                     
34209                     break;
34210                     
34211                 case 'xs' :
34212                 case 'sm' :
34213                 case 'tall' :
34214                     
34215                     box.push(item);
34216                     
34217                     break;
34218                 default :
34219                     break;
34220                     
34221             }
34222             
34223         }, this);
34224         
34225         if(box.length){
34226             boxes.push(box);
34227             box = [];
34228         }
34229         
34230         var filterPattern = function(box, length)
34231         {
34232             if(!box.length){
34233                 return;
34234             }
34235             
34236             var match = false;
34237             
34238             var pattern = box.slice(0, length);
34239             
34240             var format = [];
34241             
34242             Roo.each(pattern, function(i){
34243                 format.push(i.size);
34244             }, this);
34245             
34246             Roo.each(standard, function(s){
34247                 
34248                 if(String(s) != String(format)){
34249                     return;
34250                 }
34251                 
34252                 match = true;
34253                 return false;
34254                 
34255             }, this);
34256             
34257             if(!match && length == 1){
34258                 return;
34259             }
34260             
34261             if(!match){
34262                 filterPattern(box, length - 1);
34263                 return;
34264             }
34265                 
34266             queue.push(pattern);
34267
34268             box = box.slice(length, box.length);
34269
34270             filterPattern(box, 4);
34271
34272             return;
34273             
34274         }
34275         
34276         Roo.each(boxes, function(box, k){
34277             
34278             if(!box.length){
34279                 return;
34280             }
34281             
34282             if(box.length == 1){
34283                 queue.push(box);
34284                 return;
34285             }
34286             
34287             filterPattern(box, 4);
34288             
34289         }, this);
34290         
34291         this._processVerticalLayoutQueue( queue, isInstant );
34292         
34293     },
34294     
34295 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34296 //    {
34297 //        if ( !items || !items.length ) {
34298 //            return;
34299 //        }
34300 //
34301 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34302 //        
34303 //    },
34304     
34305     _horizontalLayoutItems : function ( items , isInstant)
34306     {
34307         if ( !items || !items.length || items.length < 3) {
34308             return;
34309         }
34310         
34311         items.reverse();
34312         
34313         var eItems = items.slice(0, 3);
34314         
34315         items = items.slice(3, items.length);
34316         
34317         var standard = [
34318             ['xs', 'xs', 'xs', 'wide'],
34319             ['xs', 'xs', 'wide'],
34320             ['xs', 'xs', 'sm'],
34321             ['xs', 'xs', 'xs'],
34322             ['xs', 'wide'],
34323             ['xs', 'sm'],
34324             ['xs', 'xs'],
34325             ['xs'],
34326             
34327             ['sm', 'xs', 'xs'],
34328             ['sm', 'xs'],
34329             ['sm'],
34330             
34331             ['wide', 'xs', 'xs', 'xs'],
34332             ['wide', 'xs', 'xs'],
34333             ['wide', 'xs'],
34334             ['wide'],
34335             
34336             ['wide-thin']
34337         ];
34338         
34339         var queue = [];
34340         
34341         var boxes = [];
34342         
34343         var box = [];
34344         
34345         Roo.each(items, function(item, k){
34346             
34347             switch (item.size) {
34348                 case 'md' :
34349                 case 'md-left' :
34350                 case 'md-right' :
34351                 case 'tall' :
34352                     
34353                     if(box.length){
34354                         boxes.push(box);
34355                         box = [];
34356                     }
34357                     
34358                     boxes.push([item]);
34359                     
34360                     break;
34361                     
34362                 case 'xs' :
34363                 case 'sm' :
34364                 case 'wide' :
34365                 case 'wide-thin' :
34366                     
34367                     box.push(item);
34368                     
34369                     break;
34370                 default :
34371                     break;
34372                     
34373             }
34374             
34375         }, this);
34376         
34377         if(box.length){
34378             boxes.push(box);
34379             box = [];
34380         }
34381         
34382         var filterPattern = function(box, length)
34383         {
34384             if(!box.length){
34385                 return;
34386             }
34387             
34388             var match = false;
34389             
34390             var pattern = box.slice(0, length);
34391             
34392             var format = [];
34393             
34394             Roo.each(pattern, function(i){
34395                 format.push(i.size);
34396             }, this);
34397             
34398             Roo.each(standard, function(s){
34399                 
34400                 if(String(s) != String(format)){
34401                     return;
34402                 }
34403                 
34404                 match = true;
34405                 return false;
34406                 
34407             }, this);
34408             
34409             if(!match && length == 1){
34410                 return;
34411             }
34412             
34413             if(!match){
34414                 filterPattern(box, length - 1);
34415                 return;
34416             }
34417                 
34418             queue.push(pattern);
34419
34420             box = box.slice(length, box.length);
34421
34422             filterPattern(box, 4);
34423
34424             return;
34425             
34426         }
34427         
34428         Roo.each(boxes, function(box, k){
34429             
34430             if(!box.length){
34431                 return;
34432             }
34433             
34434             if(box.length == 1){
34435                 queue.push(box);
34436                 return;
34437             }
34438             
34439             filterPattern(box, 4);
34440             
34441         }, this);
34442         
34443         
34444         var prune = [];
34445         
34446         var pos = this.el.getBox(true);
34447         
34448         var minX = pos.x;
34449         
34450         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34451         
34452         var hit_end = false;
34453         
34454         Roo.each(queue, function(box){
34455             
34456             if(hit_end){
34457                 
34458                 Roo.each(box, function(b){
34459                 
34460                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34461                     b.el.hide();
34462
34463                 }, this);
34464
34465                 return;
34466             }
34467             
34468             var mx = 0;
34469             
34470             Roo.each(box, function(b){
34471                 
34472                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34473                 b.el.show();
34474
34475                 mx = Math.max(mx, b.x);
34476                 
34477             }, this);
34478             
34479             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34480             
34481             if(maxX < minX){
34482                 
34483                 Roo.each(box, function(b){
34484                 
34485                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34486                     b.el.hide();
34487                     
34488                 }, this);
34489                 
34490                 hit_end = true;
34491                 
34492                 return;
34493             }
34494             
34495             prune.push(box);
34496             
34497         }, this);
34498         
34499         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34500     },
34501     
34502     /** Sets position of item in DOM
34503     * @param {Element} item
34504     * @param {Number} x - horizontal position
34505     * @param {Number} y - vertical position
34506     * @param {Boolean} isInstant - disables transitions
34507     */
34508     _processVerticalLayoutQueue : function( queue, isInstant )
34509     {
34510         var pos = this.el.getBox(true);
34511         var x = pos.x;
34512         var y = pos.y;
34513         var maxY = [];
34514         
34515         for (var i = 0; i < this.cols; i++){
34516             maxY[i] = pos.y;
34517         }
34518         
34519         Roo.each(queue, function(box, k){
34520             
34521             var col = k % this.cols;
34522             
34523             Roo.each(box, function(b,kk){
34524                 
34525                 b.el.position('absolute');
34526                 
34527                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34528                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34529                 
34530                 if(b.size == 'md-left' || b.size == 'md-right'){
34531                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34532                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34533                 }
34534                 
34535                 b.el.setWidth(width);
34536                 b.el.setHeight(height);
34537                 // iframe?
34538                 b.el.select('iframe',true).setSize(width,height);
34539                 
34540             }, this);
34541             
34542             for (var i = 0; i < this.cols; i++){
34543                 
34544                 if(maxY[i] < maxY[col]){
34545                     col = i;
34546                     continue;
34547                 }
34548                 
34549                 col = Math.min(col, i);
34550                 
34551             }
34552             
34553             x = pos.x + col * (this.colWidth + this.padWidth);
34554             
34555             y = maxY[col];
34556             
34557             var positions = [];
34558             
34559             switch (box.length){
34560                 case 1 :
34561                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34562                     break;
34563                 case 2 :
34564                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34565                     break;
34566                 case 3 :
34567                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34568                     break;
34569                 case 4 :
34570                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34571                     break;
34572                 default :
34573                     break;
34574             }
34575             
34576             Roo.each(box, function(b,kk){
34577                 
34578                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34579                 
34580                 var sz = b.el.getSize();
34581                 
34582                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34583                 
34584             }, this);
34585             
34586         }, this);
34587         
34588         var mY = 0;
34589         
34590         for (var i = 0; i < this.cols; i++){
34591             mY = Math.max(mY, maxY[i]);
34592         }
34593         
34594         this.el.setHeight(mY - pos.y);
34595         
34596     },
34597     
34598 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34599 //    {
34600 //        var pos = this.el.getBox(true);
34601 //        var x = pos.x;
34602 //        var y = pos.y;
34603 //        var maxX = pos.right;
34604 //        
34605 //        var maxHeight = 0;
34606 //        
34607 //        Roo.each(items, function(item, k){
34608 //            
34609 //            var c = k % 2;
34610 //            
34611 //            item.el.position('absolute');
34612 //                
34613 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34614 //
34615 //            item.el.setWidth(width);
34616 //
34617 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34618 //
34619 //            item.el.setHeight(height);
34620 //            
34621 //            if(c == 0){
34622 //                item.el.setXY([x, y], isInstant ? false : true);
34623 //            } else {
34624 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34625 //            }
34626 //            
34627 //            y = y + height + this.alternativePadWidth;
34628 //            
34629 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34630 //            
34631 //        }, this);
34632 //        
34633 //        this.el.setHeight(maxHeight);
34634 //        
34635 //    },
34636     
34637     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34638     {
34639         var pos = this.el.getBox(true);
34640         
34641         var minX = pos.x;
34642         var minY = pos.y;
34643         
34644         var maxX = pos.right;
34645         
34646         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34647         
34648         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34649         
34650         Roo.each(queue, function(box, k){
34651             
34652             Roo.each(box, function(b, kk){
34653                 
34654                 b.el.position('absolute');
34655                 
34656                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34657                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34658                 
34659                 if(b.size == 'md-left' || b.size == 'md-right'){
34660                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34661                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34662                 }
34663                 
34664                 b.el.setWidth(width);
34665                 b.el.setHeight(height);
34666                 
34667             }, this);
34668             
34669             if(!box.length){
34670                 return;
34671             }
34672             
34673             var positions = [];
34674             
34675             switch (box.length){
34676                 case 1 :
34677                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34678                     break;
34679                 case 2 :
34680                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34681                     break;
34682                 case 3 :
34683                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34684                     break;
34685                 case 4 :
34686                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34687                     break;
34688                 default :
34689                     break;
34690             }
34691             
34692             Roo.each(box, function(b,kk){
34693                 
34694                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34695                 
34696                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34697                 
34698             }, this);
34699             
34700         }, this);
34701         
34702     },
34703     
34704     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34705     {
34706         Roo.each(eItems, function(b,k){
34707             
34708             b.size = (k == 0) ? 'sm' : 'xs';
34709             b.x = (k == 0) ? 2 : 1;
34710             b.y = (k == 0) ? 2 : 1;
34711             
34712             b.el.position('absolute');
34713             
34714             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34715                 
34716             b.el.setWidth(width);
34717             
34718             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34719             
34720             b.el.setHeight(height);
34721             
34722         }, this);
34723
34724         var positions = [];
34725         
34726         positions.push({
34727             x : maxX - this.unitWidth * 2 - this.gutter,
34728             y : minY
34729         });
34730         
34731         positions.push({
34732             x : maxX - this.unitWidth,
34733             y : minY + (this.unitWidth + this.gutter) * 2
34734         });
34735         
34736         positions.push({
34737             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34738             y : minY
34739         });
34740         
34741         Roo.each(eItems, function(b,k){
34742             
34743             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34744
34745         }, this);
34746         
34747     },
34748     
34749     getVerticalOneBoxColPositions : function(x, y, box)
34750     {
34751         var pos = [];
34752         
34753         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34754         
34755         if(box[0].size == 'md-left'){
34756             rand = 0;
34757         }
34758         
34759         if(box[0].size == 'md-right'){
34760             rand = 1;
34761         }
34762         
34763         pos.push({
34764             x : x + (this.unitWidth + this.gutter) * rand,
34765             y : y
34766         });
34767         
34768         return pos;
34769     },
34770     
34771     getVerticalTwoBoxColPositions : function(x, y, box)
34772     {
34773         var pos = [];
34774         
34775         if(box[0].size == 'xs'){
34776             
34777             pos.push({
34778                 x : x,
34779                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34780             });
34781
34782             pos.push({
34783                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34784                 y : y
34785             });
34786             
34787             return pos;
34788             
34789         }
34790         
34791         pos.push({
34792             x : x,
34793             y : y
34794         });
34795
34796         pos.push({
34797             x : x + (this.unitWidth + this.gutter) * 2,
34798             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34799         });
34800         
34801         return pos;
34802         
34803     },
34804     
34805     getVerticalThreeBoxColPositions : function(x, y, box)
34806     {
34807         var pos = [];
34808         
34809         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34810             
34811             pos.push({
34812                 x : x,
34813                 y : y
34814             });
34815
34816             pos.push({
34817                 x : x + (this.unitWidth + this.gutter) * 1,
34818                 y : y
34819             });
34820             
34821             pos.push({
34822                 x : x + (this.unitWidth + this.gutter) * 2,
34823                 y : y
34824             });
34825             
34826             return pos;
34827             
34828         }
34829         
34830         if(box[0].size == 'xs' && box[1].size == 'xs'){
34831             
34832             pos.push({
34833                 x : x,
34834                 y : y
34835             });
34836
34837             pos.push({
34838                 x : x,
34839                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34840             });
34841             
34842             pos.push({
34843                 x : x + (this.unitWidth + this.gutter) * 1,
34844                 y : y
34845             });
34846             
34847             return pos;
34848             
34849         }
34850         
34851         pos.push({
34852             x : x,
34853             y : y
34854         });
34855
34856         pos.push({
34857             x : x + (this.unitWidth + this.gutter) * 2,
34858             y : y
34859         });
34860
34861         pos.push({
34862             x : x + (this.unitWidth + this.gutter) * 2,
34863             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34864         });
34865             
34866         return pos;
34867         
34868     },
34869     
34870     getVerticalFourBoxColPositions : function(x, y, box)
34871     {
34872         var pos = [];
34873         
34874         if(box[0].size == 'xs'){
34875             
34876             pos.push({
34877                 x : x,
34878                 y : y
34879             });
34880
34881             pos.push({
34882                 x : x,
34883                 y : y + (this.unitHeight + this.gutter) * 1
34884             });
34885             
34886             pos.push({
34887                 x : x,
34888                 y : y + (this.unitHeight + this.gutter) * 2
34889             });
34890             
34891             pos.push({
34892                 x : x + (this.unitWidth + this.gutter) * 1,
34893                 y : y
34894             });
34895             
34896             return pos;
34897             
34898         }
34899         
34900         pos.push({
34901             x : x,
34902             y : y
34903         });
34904
34905         pos.push({
34906             x : x + (this.unitWidth + this.gutter) * 2,
34907             y : y
34908         });
34909
34910         pos.push({
34911             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34912             y : y + (this.unitHeight + this.gutter) * 1
34913         });
34914
34915         pos.push({
34916             x : x + (this.unitWidth + this.gutter) * 2,
34917             y : y + (this.unitWidth + this.gutter) * 2
34918         });
34919
34920         return pos;
34921         
34922     },
34923     
34924     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34925     {
34926         var pos = [];
34927         
34928         if(box[0].size == 'md-left'){
34929             pos.push({
34930                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34931                 y : minY
34932             });
34933             
34934             return pos;
34935         }
34936         
34937         if(box[0].size == 'md-right'){
34938             pos.push({
34939                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34940                 y : minY + (this.unitWidth + this.gutter) * 1
34941             });
34942             
34943             return pos;
34944         }
34945         
34946         var rand = Math.floor(Math.random() * (4 - box[0].y));
34947         
34948         pos.push({
34949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34950             y : minY + (this.unitWidth + this.gutter) * rand
34951         });
34952         
34953         return pos;
34954         
34955     },
34956     
34957     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34958     {
34959         var pos = [];
34960         
34961         if(box[0].size == 'xs'){
34962             
34963             pos.push({
34964                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34965                 y : minY
34966             });
34967
34968             pos.push({
34969                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34971             });
34972             
34973             return pos;
34974             
34975         }
34976         
34977         pos.push({
34978             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34979             y : minY
34980         });
34981
34982         pos.push({
34983             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984             y : minY + (this.unitWidth + this.gutter) * 2
34985         });
34986         
34987         return pos;
34988         
34989     },
34990     
34991     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34992     {
34993         var pos = [];
34994         
34995         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34996             
34997             pos.push({
34998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34999                 y : minY
35000             });
35001
35002             pos.push({
35003                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004                 y : minY + (this.unitWidth + this.gutter) * 1
35005             });
35006             
35007             pos.push({
35008                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009                 y : minY + (this.unitWidth + this.gutter) * 2
35010             });
35011             
35012             return pos;
35013             
35014         }
35015         
35016         if(box[0].size == 'xs' && box[1].size == 'xs'){
35017             
35018             pos.push({
35019                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35020                 y : minY
35021             });
35022
35023             pos.push({
35024                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35025                 y : minY
35026             });
35027             
35028             pos.push({
35029                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35030                 y : minY + (this.unitWidth + this.gutter) * 1
35031             });
35032             
35033             return pos;
35034             
35035         }
35036         
35037         pos.push({
35038             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35039             y : minY
35040         });
35041
35042         pos.push({
35043             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35044             y : minY + (this.unitWidth + this.gutter) * 2
35045         });
35046
35047         pos.push({
35048             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35049             y : minY + (this.unitWidth + this.gutter) * 2
35050         });
35051             
35052         return pos;
35053         
35054     },
35055     
35056     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35057     {
35058         var pos = [];
35059         
35060         if(box[0].size == 'xs'){
35061             
35062             pos.push({
35063                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35064                 y : minY
35065             });
35066
35067             pos.push({
35068                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35069                 y : minY
35070             });
35071             
35072             pos.push({
35073                 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),
35074                 y : minY
35075             });
35076             
35077             pos.push({
35078                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35079                 y : minY + (this.unitWidth + this.gutter) * 1
35080             });
35081             
35082             return pos;
35083             
35084         }
35085         
35086         pos.push({
35087             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35088             y : minY
35089         });
35090         
35091         pos.push({
35092             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093             y : minY + (this.unitWidth + this.gutter) * 2
35094         });
35095         
35096         pos.push({
35097             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35098             y : minY + (this.unitWidth + this.gutter) * 2
35099         });
35100         
35101         pos.push({
35102             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),
35103             y : minY + (this.unitWidth + this.gutter) * 2
35104         });
35105
35106         return pos;
35107         
35108     },
35109     
35110     /**
35111     * remove a Masonry Brick
35112     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35113     */
35114     removeBrick : function(brick_id)
35115     {
35116         if (!brick_id) {
35117             return;
35118         }
35119         
35120         for (var i = 0; i<this.bricks.length; i++) {
35121             if (this.bricks[i].id == brick_id) {
35122                 this.bricks.splice(i,1);
35123                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35124                 this.initial();
35125             }
35126         }
35127     },
35128     
35129     /**
35130     * adds a Masonry Brick
35131     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35132     */
35133     addBrick : function(cfg)
35134     {
35135         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35136         //this.register(cn);
35137         cn.parentId = this.id;
35138         cn.render(this.el);
35139         return cn;
35140     },
35141     
35142     /**
35143     * register a Masonry Brick
35144     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35145     */
35146     
35147     register : function(brick)
35148     {
35149         this.bricks.push(brick);
35150         brick.masonryId = this.id;
35151     },
35152     
35153     /**
35154     * clear all the Masonry Brick
35155     */
35156     clearAll : function()
35157     {
35158         this.bricks = [];
35159         //this.getChildContainer().dom.innerHTML = "";
35160         this.el.dom.innerHTML = '';
35161     },
35162     
35163     getSelected : function()
35164     {
35165         if (!this.selectedBrick) {
35166             return false;
35167         }
35168         
35169         return this.selectedBrick;
35170     }
35171 });
35172
35173 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35174     
35175     groups: {},
35176      /**
35177     * register a Masonry Layout
35178     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35179     */
35180     
35181     register : function(layout)
35182     {
35183         this.groups[layout.id] = layout;
35184     },
35185     /**
35186     * fetch a  Masonry Layout based on the masonry layout ID
35187     * @param {string} the masonry layout to add
35188     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35189     */
35190     
35191     get: function(layout_id) {
35192         if (typeof(this.groups[layout_id]) == 'undefined') {
35193             return false;
35194         }
35195         return this.groups[layout_id] ;
35196     }
35197     
35198     
35199     
35200 });
35201
35202  
35203
35204  /**
35205  *
35206  * This is based on 
35207  * http://masonry.desandro.com
35208  *
35209  * The idea is to render all the bricks based on vertical width...
35210  *
35211  * The original code extends 'outlayer' - we might need to use that....
35212  * 
35213  */
35214
35215
35216 /**
35217  * @class Roo.bootstrap.LayoutMasonryAuto
35218  * @extends Roo.bootstrap.Component
35219  * Bootstrap Layout Masonry class
35220  * 
35221  * @constructor
35222  * Create a new Element
35223  * @param {Object} config The config object
35224  */
35225
35226 Roo.bootstrap.LayoutMasonryAuto = function(config){
35227     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35228 };
35229
35230 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35231     
35232       /**
35233      * @cfg {Boolean} isFitWidth  - resize the width..
35234      */   
35235     isFitWidth : false,  // options..
35236     /**
35237      * @cfg {Boolean} isOriginLeft = left align?
35238      */   
35239     isOriginLeft : true,
35240     /**
35241      * @cfg {Boolean} isOriginTop = top align?
35242      */   
35243     isOriginTop : false,
35244     /**
35245      * @cfg {Boolean} isLayoutInstant = no animation?
35246      */   
35247     isLayoutInstant : false, // needed?
35248     /**
35249      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35250      */   
35251     isResizingContainer : true,
35252     /**
35253      * @cfg {Number} columnWidth  width of the columns 
35254      */   
35255     
35256     columnWidth : 0,
35257     
35258     /**
35259      * @cfg {Number} maxCols maximum number of columns
35260      */   
35261     
35262     maxCols: 0,
35263     /**
35264      * @cfg {Number} padHeight padding below box..
35265      */   
35266     
35267     padHeight : 10, 
35268     
35269     /**
35270      * @cfg {Boolean} isAutoInitial defalut true
35271      */   
35272     
35273     isAutoInitial : true, 
35274     
35275     // private?
35276     gutter : 0,
35277     
35278     containerWidth: 0,
35279     initialColumnWidth : 0,
35280     currentSize : null,
35281     
35282     colYs : null, // array.
35283     maxY : 0,
35284     padWidth: 10,
35285     
35286     
35287     tag: 'div',
35288     cls: '',
35289     bricks: null, //CompositeElement
35290     cols : 0, // array?
35291     // element : null, // wrapped now this.el
35292     _isLayoutInited : null, 
35293     
35294     
35295     getAutoCreate : function(){
35296         
35297         var cfg = {
35298             tag: this.tag,
35299             cls: 'blog-masonary-wrapper ' + this.cls,
35300             cn : {
35301                 cls : 'mas-boxes masonary'
35302             }
35303         };
35304         
35305         return cfg;
35306     },
35307     
35308     getChildContainer: function( )
35309     {
35310         if (this.boxesEl) {
35311             return this.boxesEl;
35312         }
35313         
35314         this.boxesEl = this.el.select('.mas-boxes').first();
35315         
35316         return this.boxesEl;
35317     },
35318     
35319     
35320     initEvents : function()
35321     {
35322         var _this = this;
35323         
35324         if(this.isAutoInitial){
35325             Roo.log('hook children rendered');
35326             this.on('childrenrendered', function() {
35327                 Roo.log('children rendered');
35328                 _this.initial();
35329             } ,this);
35330         }
35331         
35332     },
35333     
35334     initial : function()
35335     {
35336         this.reloadItems();
35337
35338         this.currentSize = this.el.getBox(true);
35339
35340         /// was window resize... - let's see if this works..
35341         Roo.EventManager.onWindowResize(this.resize, this); 
35342
35343         if(!this.isAutoInitial){
35344             this.layout();
35345             return;
35346         }
35347         
35348         this.layout.defer(500,this);
35349     },
35350     
35351     reloadItems: function()
35352     {
35353         this.bricks = this.el.select('.masonry-brick', true);
35354         
35355         this.bricks.each(function(b) {
35356             //Roo.log(b.getSize());
35357             if (!b.attr('originalwidth')) {
35358                 b.attr('originalwidth',  b.getSize().width);
35359             }
35360             
35361         });
35362         
35363         Roo.log(this.bricks.elements.length);
35364     },
35365     
35366     resize : function()
35367     {
35368         Roo.log('resize');
35369         var cs = this.el.getBox(true);
35370         
35371         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35372             Roo.log("no change in with or X");
35373             return;
35374         }
35375         this.currentSize = cs;
35376         this.layout();
35377     },
35378     
35379     layout : function()
35380     {
35381          Roo.log('layout');
35382         this._resetLayout();
35383         //this._manageStamps();
35384       
35385         // don't animate first layout
35386         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35387         this.layoutItems( isInstant );
35388       
35389         // flag for initalized
35390         this._isLayoutInited = true;
35391     },
35392     
35393     layoutItems : function( isInstant )
35394     {
35395         //var items = this._getItemsForLayout( this.items );
35396         // original code supports filtering layout items.. we just ignore it..
35397         
35398         this._layoutItems( this.bricks , isInstant );
35399       
35400         this._postLayout();
35401     },
35402     _layoutItems : function ( items , isInstant)
35403     {
35404        //this.fireEvent( 'layout', this, items );
35405     
35406
35407         if ( !items || !items.elements.length ) {
35408           // no items, emit event with empty array
35409             return;
35410         }
35411
35412         var queue = [];
35413         items.each(function(item) {
35414             Roo.log("layout item");
35415             Roo.log(item);
35416             // get x/y object from method
35417             var position = this._getItemLayoutPosition( item );
35418             // enqueue
35419             position.item = item;
35420             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35421             queue.push( position );
35422         }, this);
35423       
35424         this._processLayoutQueue( queue );
35425     },
35426     /** Sets position of item in DOM
35427     * @param {Element} item
35428     * @param {Number} x - horizontal position
35429     * @param {Number} y - vertical position
35430     * @param {Boolean} isInstant - disables transitions
35431     */
35432     _processLayoutQueue : function( queue )
35433     {
35434         for ( var i=0, len = queue.length; i < len; i++ ) {
35435             var obj = queue[i];
35436             obj.item.position('absolute');
35437             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35438         }
35439     },
35440       
35441     
35442     /**
35443     * Any logic you want to do after each layout,
35444     * i.e. size the container
35445     */
35446     _postLayout : function()
35447     {
35448         this.resizeContainer();
35449     },
35450     
35451     resizeContainer : function()
35452     {
35453         if ( !this.isResizingContainer ) {
35454             return;
35455         }
35456         var size = this._getContainerSize();
35457         if ( size ) {
35458             this.el.setSize(size.width,size.height);
35459             this.boxesEl.setSize(size.width,size.height);
35460         }
35461     },
35462     
35463     
35464     
35465     _resetLayout : function()
35466     {
35467         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35468         this.colWidth = this.el.getWidth();
35469         //this.gutter = this.el.getWidth(); 
35470         
35471         this.measureColumns();
35472
35473         // reset column Y
35474         var i = this.cols;
35475         this.colYs = [];
35476         while (i--) {
35477             this.colYs.push( 0 );
35478         }
35479     
35480         this.maxY = 0;
35481     },
35482
35483     measureColumns : function()
35484     {
35485         this.getContainerWidth();
35486       // if columnWidth is 0, default to outerWidth of first item
35487         if ( !this.columnWidth ) {
35488             var firstItem = this.bricks.first();
35489             Roo.log(firstItem);
35490             this.columnWidth  = this.containerWidth;
35491             if (firstItem && firstItem.attr('originalwidth') ) {
35492                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35493             }
35494             // columnWidth fall back to item of first element
35495             Roo.log("set column width?");
35496                         this.initialColumnWidth = this.columnWidth  ;
35497
35498             // if first elem has no width, default to size of container
35499             
35500         }
35501         
35502         
35503         if (this.initialColumnWidth) {
35504             this.columnWidth = this.initialColumnWidth;
35505         }
35506         
35507         
35508             
35509         // column width is fixed at the top - however if container width get's smaller we should
35510         // reduce it...
35511         
35512         // this bit calcs how man columns..
35513             
35514         var columnWidth = this.columnWidth += this.gutter;
35515       
35516         // calculate columns
35517         var containerWidth = this.containerWidth + this.gutter;
35518         
35519         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35520         // fix rounding errors, typically with gutters
35521         var excess = columnWidth - containerWidth % columnWidth;
35522         
35523         
35524         // if overshoot is less than a pixel, round up, otherwise floor it
35525         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35526         cols = Math[ mathMethod ]( cols );
35527         this.cols = Math.max( cols, 1 );
35528         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35529         
35530          // padding positioning..
35531         var totalColWidth = this.cols * this.columnWidth;
35532         var padavail = this.containerWidth - totalColWidth;
35533         // so for 2 columns - we need 3 'pads'
35534         
35535         var padNeeded = (1+this.cols) * this.padWidth;
35536         
35537         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35538         
35539         this.columnWidth += padExtra
35540         //this.padWidth = Math.floor(padavail /  ( this.cols));
35541         
35542         // adjust colum width so that padding is fixed??
35543         
35544         // we have 3 columns ... total = width * 3
35545         // we have X left over... that should be used by 
35546         
35547         //if (this.expandC) {
35548             
35549         //}
35550         
35551         
35552         
35553     },
35554     
35555     getContainerWidth : function()
35556     {
35557        /* // container is parent if fit width
35558         var container = this.isFitWidth ? this.element.parentNode : this.element;
35559         // check that this.size and size are there
35560         // IE8 triggers resize on body size change, so they might not be
35561         
35562         var size = getSize( container );  //FIXME
35563         this.containerWidth = size && size.innerWidth; //FIXME
35564         */
35565          
35566         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35567         
35568     },
35569     
35570     _getItemLayoutPosition : function( item )  // what is item?
35571     {
35572         // we resize the item to our columnWidth..
35573       
35574         item.setWidth(this.columnWidth);
35575         item.autoBoxAdjust  = false;
35576         
35577         var sz = item.getSize();
35578  
35579         // how many columns does this brick span
35580         var remainder = this.containerWidth % this.columnWidth;
35581         
35582         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35583         // round if off by 1 pixel, otherwise use ceil
35584         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35585         colSpan = Math.min( colSpan, this.cols );
35586         
35587         // normally this should be '1' as we dont' currently allow multi width columns..
35588         
35589         var colGroup = this._getColGroup( colSpan );
35590         // get the minimum Y value from the columns
35591         var minimumY = Math.min.apply( Math, colGroup );
35592         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35593         
35594         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35595          
35596         // position the brick
35597         var position = {
35598             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35599             y: this.currentSize.y + minimumY + this.padHeight
35600         };
35601         
35602         Roo.log(position);
35603         // apply setHeight to necessary columns
35604         var setHeight = minimumY + sz.height + this.padHeight;
35605         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35606         
35607         var setSpan = this.cols + 1 - colGroup.length;
35608         for ( var i = 0; i < setSpan; i++ ) {
35609           this.colYs[ shortColIndex + i ] = setHeight ;
35610         }
35611       
35612         return position;
35613     },
35614     
35615     /**
35616      * @param {Number} colSpan - number of columns the element spans
35617      * @returns {Array} colGroup
35618      */
35619     _getColGroup : function( colSpan )
35620     {
35621         if ( colSpan < 2 ) {
35622           // if brick spans only one column, use all the column Ys
35623           return this.colYs;
35624         }
35625       
35626         var colGroup = [];
35627         // how many different places could this brick fit horizontally
35628         var groupCount = this.cols + 1 - colSpan;
35629         // for each group potential horizontal position
35630         for ( var i = 0; i < groupCount; i++ ) {
35631           // make an array of colY values for that one group
35632           var groupColYs = this.colYs.slice( i, i + colSpan );
35633           // and get the max value of the array
35634           colGroup[i] = Math.max.apply( Math, groupColYs );
35635         }
35636         return colGroup;
35637     },
35638     /*
35639     _manageStamp : function( stamp )
35640     {
35641         var stampSize =  stamp.getSize();
35642         var offset = stamp.getBox();
35643         // get the columns that this stamp affects
35644         var firstX = this.isOriginLeft ? offset.x : offset.right;
35645         var lastX = firstX + stampSize.width;
35646         var firstCol = Math.floor( firstX / this.columnWidth );
35647         firstCol = Math.max( 0, firstCol );
35648         
35649         var lastCol = Math.floor( lastX / this.columnWidth );
35650         // lastCol should not go over if multiple of columnWidth #425
35651         lastCol -= lastX % this.columnWidth ? 0 : 1;
35652         lastCol = Math.min( this.cols - 1, lastCol );
35653         
35654         // set colYs to bottom of the stamp
35655         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35656             stampSize.height;
35657             
35658         for ( var i = firstCol; i <= lastCol; i++ ) {
35659           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35660         }
35661     },
35662     */
35663     
35664     _getContainerSize : function()
35665     {
35666         this.maxY = Math.max.apply( Math, this.colYs );
35667         var size = {
35668             height: this.maxY
35669         };
35670       
35671         if ( this.isFitWidth ) {
35672             size.width = this._getContainerFitWidth();
35673         }
35674       
35675         return size;
35676     },
35677     
35678     _getContainerFitWidth : function()
35679     {
35680         var unusedCols = 0;
35681         // count unused columns
35682         var i = this.cols;
35683         while ( --i ) {
35684           if ( this.colYs[i] !== 0 ) {
35685             break;
35686           }
35687           unusedCols++;
35688         }
35689         // fit container to columns that have been used
35690         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35691     },
35692     
35693     needsResizeLayout : function()
35694     {
35695         var previousWidth = this.containerWidth;
35696         this.getContainerWidth();
35697         return previousWidth !== this.containerWidth;
35698     }
35699  
35700 });
35701
35702  
35703
35704  /*
35705  * - LGPL
35706  *
35707  * element
35708  * 
35709  */
35710
35711 /**
35712  * @class Roo.bootstrap.MasonryBrick
35713  * @extends Roo.bootstrap.Component
35714  * Bootstrap MasonryBrick class
35715  * 
35716  * @constructor
35717  * Create a new MasonryBrick
35718  * @param {Object} config The config object
35719  */
35720
35721 Roo.bootstrap.MasonryBrick = function(config){
35722     
35723     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35724     
35725     Roo.bootstrap.MasonryBrick.register(this);
35726     
35727     this.addEvents({
35728         // raw events
35729         /**
35730          * @event click
35731          * When a MasonryBrick is clcik
35732          * @param {Roo.bootstrap.MasonryBrick} this
35733          * @param {Roo.EventObject} e
35734          */
35735         "click" : true
35736     });
35737 };
35738
35739 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35740     
35741     /**
35742      * @cfg {String} title
35743      */   
35744     title : '',
35745     /**
35746      * @cfg {String} html
35747      */   
35748     html : '',
35749     /**
35750      * @cfg {String} bgimage
35751      */   
35752     bgimage : '',
35753     /**
35754      * @cfg {String} videourl
35755      */   
35756     videourl : '',
35757     /**
35758      * @cfg {String} cls
35759      */   
35760     cls : '',
35761     /**
35762      * @cfg {String} href
35763      */   
35764     href : '',
35765     /**
35766      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35767      */   
35768     size : 'xs',
35769     
35770     /**
35771      * @cfg {String} placetitle (center|bottom)
35772      */   
35773     placetitle : '',
35774     
35775     /**
35776      * @cfg {Boolean} isFitContainer defalut true
35777      */   
35778     isFitContainer : true, 
35779     
35780     /**
35781      * @cfg {Boolean} preventDefault defalut false
35782      */   
35783     preventDefault : false, 
35784     
35785     /**
35786      * @cfg {Boolean} inverse defalut false
35787      */   
35788     maskInverse : false, 
35789     
35790     getAutoCreate : function()
35791     {
35792         if(!this.isFitContainer){
35793             return this.getSplitAutoCreate();
35794         }
35795         
35796         var cls = 'masonry-brick masonry-brick-full';
35797         
35798         if(this.href.length){
35799             cls += ' masonry-brick-link';
35800         }
35801         
35802         if(this.bgimage.length){
35803             cls += ' masonry-brick-image';
35804         }
35805         
35806         if(this.maskInverse){
35807             cls += ' mask-inverse';
35808         }
35809         
35810         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35811             cls += ' enable-mask';
35812         }
35813         
35814         if(this.size){
35815             cls += ' masonry-' + this.size + '-brick';
35816         }
35817         
35818         if(this.placetitle.length){
35819             
35820             switch (this.placetitle) {
35821                 case 'center' :
35822                     cls += ' masonry-center-title';
35823                     break;
35824                 case 'bottom' :
35825                     cls += ' masonry-bottom-title';
35826                     break;
35827                 default:
35828                     break;
35829             }
35830             
35831         } else {
35832             if(!this.html.length && !this.bgimage.length){
35833                 cls += ' masonry-center-title';
35834             }
35835
35836             if(!this.html.length && this.bgimage.length){
35837                 cls += ' masonry-bottom-title';
35838             }
35839         }
35840         
35841         if(this.cls){
35842             cls += ' ' + this.cls;
35843         }
35844         
35845         var cfg = {
35846             tag: (this.href.length) ? 'a' : 'div',
35847             cls: cls,
35848             cn: [
35849                 {
35850                     tag: 'div',
35851                     cls: 'masonry-brick-mask'
35852                 },
35853                 {
35854                     tag: 'div',
35855                     cls: 'masonry-brick-paragraph',
35856                     cn: []
35857                 }
35858             ]
35859         };
35860         
35861         if(this.href.length){
35862             cfg.href = this.href;
35863         }
35864         
35865         var cn = cfg.cn[1].cn;
35866         
35867         if(this.title.length){
35868             cn.push({
35869                 tag: 'h4',
35870                 cls: 'masonry-brick-title',
35871                 html: this.title
35872             });
35873         }
35874         
35875         if(this.html.length){
35876             cn.push({
35877                 tag: 'p',
35878                 cls: 'masonry-brick-text',
35879                 html: this.html
35880             });
35881         }
35882         
35883         if (!this.title.length && !this.html.length) {
35884             cfg.cn[1].cls += ' hide';
35885         }
35886         
35887         if(this.bgimage.length){
35888             cfg.cn.push({
35889                 tag: 'img',
35890                 cls: 'masonry-brick-image-view',
35891                 src: this.bgimage
35892             });
35893         }
35894         
35895         if(this.videourl.length){
35896             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35897             // youtube support only?
35898             cfg.cn.push({
35899                 tag: 'iframe',
35900                 cls: 'masonry-brick-image-view',
35901                 src: vurl,
35902                 frameborder : 0,
35903                 allowfullscreen : true
35904             });
35905         }
35906         
35907         return cfg;
35908         
35909     },
35910     
35911     getSplitAutoCreate : function()
35912     {
35913         var cls = 'masonry-brick masonry-brick-split';
35914         
35915         if(this.href.length){
35916             cls += ' masonry-brick-link';
35917         }
35918         
35919         if(this.bgimage.length){
35920             cls += ' masonry-brick-image';
35921         }
35922         
35923         if(this.size){
35924             cls += ' masonry-' + this.size + '-brick';
35925         }
35926         
35927         switch (this.placetitle) {
35928             case 'center' :
35929                 cls += ' masonry-center-title';
35930                 break;
35931             case 'bottom' :
35932                 cls += ' masonry-bottom-title';
35933                 break;
35934             default:
35935                 if(!this.bgimage.length){
35936                     cls += ' masonry-center-title';
35937                 }
35938
35939                 if(this.bgimage.length){
35940                     cls += ' masonry-bottom-title';
35941                 }
35942                 break;
35943         }
35944         
35945         if(this.cls){
35946             cls += ' ' + this.cls;
35947         }
35948         
35949         var cfg = {
35950             tag: (this.href.length) ? 'a' : 'div',
35951             cls: cls,
35952             cn: [
35953                 {
35954                     tag: 'div',
35955                     cls: 'masonry-brick-split-head',
35956                     cn: [
35957                         {
35958                             tag: 'div',
35959                             cls: 'masonry-brick-paragraph',
35960                             cn: []
35961                         }
35962                     ]
35963                 },
35964                 {
35965                     tag: 'div',
35966                     cls: 'masonry-brick-split-body',
35967                     cn: []
35968                 }
35969             ]
35970         };
35971         
35972         if(this.href.length){
35973             cfg.href = this.href;
35974         }
35975         
35976         if(this.title.length){
35977             cfg.cn[0].cn[0].cn.push({
35978                 tag: 'h4',
35979                 cls: 'masonry-brick-title',
35980                 html: this.title
35981             });
35982         }
35983         
35984         if(this.html.length){
35985             cfg.cn[1].cn.push({
35986                 tag: 'p',
35987                 cls: 'masonry-brick-text',
35988                 html: this.html
35989             });
35990         }
35991
35992         if(this.bgimage.length){
35993             cfg.cn[0].cn.push({
35994                 tag: 'img',
35995                 cls: 'masonry-brick-image-view',
35996                 src: this.bgimage
35997             });
35998         }
35999         
36000         if(this.videourl.length){
36001             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36002             // youtube support only?
36003             cfg.cn[0].cn.cn.push({
36004                 tag: 'iframe',
36005                 cls: 'masonry-brick-image-view',
36006                 src: vurl,
36007                 frameborder : 0,
36008                 allowfullscreen : true
36009             });
36010         }
36011         
36012         return cfg;
36013     },
36014     
36015     initEvents: function() 
36016     {
36017         switch (this.size) {
36018             case 'xs' :
36019                 this.x = 1;
36020                 this.y = 1;
36021                 break;
36022             case 'sm' :
36023                 this.x = 2;
36024                 this.y = 2;
36025                 break;
36026             case 'md' :
36027             case 'md-left' :
36028             case 'md-right' :
36029                 this.x = 3;
36030                 this.y = 3;
36031                 break;
36032             case 'tall' :
36033                 this.x = 2;
36034                 this.y = 3;
36035                 break;
36036             case 'wide' :
36037                 this.x = 3;
36038                 this.y = 2;
36039                 break;
36040             case 'wide-thin' :
36041                 this.x = 3;
36042                 this.y = 1;
36043                 break;
36044                         
36045             default :
36046                 break;
36047         }
36048         
36049         if(Roo.isTouch){
36050             this.el.on('touchstart', this.onTouchStart, this);
36051             this.el.on('touchmove', this.onTouchMove, this);
36052             this.el.on('touchend', this.onTouchEnd, this);
36053             this.el.on('contextmenu', this.onContextMenu, this);
36054         } else {
36055             this.el.on('mouseenter'  ,this.enter, this);
36056             this.el.on('mouseleave', this.leave, this);
36057             this.el.on('click', this.onClick, this);
36058         }
36059         
36060         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36061             this.parent().bricks.push(this);   
36062         }
36063         
36064     },
36065     
36066     onClick: function(e, el)
36067     {
36068         var time = this.endTimer - this.startTimer;
36069         // Roo.log(e.preventDefault());
36070         if(Roo.isTouch){
36071             if(time > 1000){
36072                 e.preventDefault();
36073                 return;
36074             }
36075         }
36076         
36077         if(!this.preventDefault){
36078             return;
36079         }
36080         
36081         e.preventDefault();
36082         
36083         if (this.activeClass != '') {
36084             this.selectBrick();
36085         }
36086         
36087         this.fireEvent('click', this, e);
36088     },
36089     
36090     enter: function(e, el)
36091     {
36092         e.preventDefault();
36093         
36094         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36095             return;
36096         }
36097         
36098         if(this.bgimage.length && this.html.length){
36099             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36100         }
36101     },
36102     
36103     leave: function(e, el)
36104     {
36105         e.preventDefault();
36106         
36107         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36108             return;
36109         }
36110         
36111         if(this.bgimage.length && this.html.length){
36112             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36113         }
36114     },
36115     
36116     onTouchStart: function(e, el)
36117     {
36118 //        e.preventDefault();
36119         
36120         this.touchmoved = false;
36121         
36122         if(!this.isFitContainer){
36123             return;
36124         }
36125         
36126         if(!this.bgimage.length || !this.html.length){
36127             return;
36128         }
36129         
36130         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36131         
36132         this.timer = new Date().getTime();
36133         
36134     },
36135     
36136     onTouchMove: function(e, el)
36137     {
36138         this.touchmoved = true;
36139     },
36140     
36141     onContextMenu : function(e,el)
36142     {
36143         e.preventDefault();
36144         e.stopPropagation();
36145         return false;
36146     },
36147     
36148     onTouchEnd: function(e, el)
36149     {
36150 //        e.preventDefault();
36151         
36152         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36153         
36154             this.leave(e,el);
36155             
36156             return;
36157         }
36158         
36159         if(!this.bgimage.length || !this.html.length){
36160             
36161             if(this.href.length){
36162                 window.location.href = this.href;
36163             }
36164             
36165             return;
36166         }
36167         
36168         if(!this.isFitContainer){
36169             return;
36170         }
36171         
36172         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36173         
36174         window.location.href = this.href;
36175     },
36176     
36177     //selection on single brick only
36178     selectBrick : function() {
36179         
36180         if (!this.parentId) {
36181             return;
36182         }
36183         
36184         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36185         var index = m.selectedBrick.indexOf(this.id);
36186         
36187         if ( index > -1) {
36188             m.selectedBrick.splice(index,1);
36189             this.el.removeClass(this.activeClass);
36190             return;
36191         }
36192         
36193         for(var i = 0; i < m.selectedBrick.length; i++) {
36194             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36195             b.el.removeClass(b.activeClass);
36196         }
36197         
36198         m.selectedBrick = [];
36199         
36200         m.selectedBrick.push(this.id);
36201         this.el.addClass(this.activeClass);
36202         return;
36203     },
36204     
36205     isSelected : function(){
36206         return this.el.hasClass(this.activeClass);
36207         
36208     }
36209 });
36210
36211 Roo.apply(Roo.bootstrap.MasonryBrick, {
36212     
36213     //groups: {},
36214     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36215      /**
36216     * register a Masonry Brick
36217     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36218     */
36219     
36220     register : function(brick)
36221     {
36222         //this.groups[brick.id] = brick;
36223         this.groups.add(brick.id, brick);
36224     },
36225     /**
36226     * fetch a  masonry brick based on the masonry brick ID
36227     * @param {string} the masonry brick to add
36228     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36229     */
36230     
36231     get: function(brick_id) 
36232     {
36233         // if (typeof(this.groups[brick_id]) == 'undefined') {
36234         //     return false;
36235         // }
36236         // return this.groups[brick_id] ;
36237         
36238         if(this.groups.key(brick_id)) {
36239             return this.groups.key(brick_id);
36240         }
36241         
36242         return false;
36243     }
36244     
36245     
36246     
36247 });
36248
36249  /*
36250  * - LGPL
36251  *
36252  * element
36253  * 
36254  */
36255
36256 /**
36257  * @class Roo.bootstrap.Brick
36258  * @extends Roo.bootstrap.Component
36259  * Bootstrap Brick class
36260  * 
36261  * @constructor
36262  * Create a new Brick
36263  * @param {Object} config The config object
36264  */
36265
36266 Roo.bootstrap.Brick = function(config){
36267     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36268     
36269     this.addEvents({
36270         // raw events
36271         /**
36272          * @event click
36273          * When a Brick is click
36274          * @param {Roo.bootstrap.Brick} this
36275          * @param {Roo.EventObject} e
36276          */
36277         "click" : true
36278     });
36279 };
36280
36281 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36282     
36283     /**
36284      * @cfg {String} title
36285      */   
36286     title : '',
36287     /**
36288      * @cfg {String} html
36289      */   
36290     html : '',
36291     /**
36292      * @cfg {String} bgimage
36293      */   
36294     bgimage : '',
36295     /**
36296      * @cfg {String} cls
36297      */   
36298     cls : '',
36299     /**
36300      * @cfg {String} href
36301      */   
36302     href : '',
36303     /**
36304      * @cfg {String} video
36305      */   
36306     video : '',
36307     /**
36308      * @cfg {Boolean} square
36309      */   
36310     square : true,
36311     
36312     getAutoCreate : function()
36313     {
36314         var cls = 'roo-brick';
36315         
36316         if(this.href.length){
36317             cls += ' roo-brick-link';
36318         }
36319         
36320         if(this.bgimage.length){
36321             cls += ' roo-brick-image';
36322         }
36323         
36324         if(!this.html.length && !this.bgimage.length){
36325             cls += ' roo-brick-center-title';
36326         }
36327         
36328         if(!this.html.length && this.bgimage.length){
36329             cls += ' roo-brick-bottom-title';
36330         }
36331         
36332         if(this.cls){
36333             cls += ' ' + this.cls;
36334         }
36335         
36336         var cfg = {
36337             tag: (this.href.length) ? 'a' : 'div',
36338             cls: cls,
36339             cn: [
36340                 {
36341                     tag: 'div',
36342                     cls: 'roo-brick-paragraph',
36343                     cn: []
36344                 }
36345             ]
36346         };
36347         
36348         if(this.href.length){
36349             cfg.href = this.href;
36350         }
36351         
36352         var cn = cfg.cn[0].cn;
36353         
36354         if(this.title.length){
36355             cn.push({
36356                 tag: 'h4',
36357                 cls: 'roo-brick-title',
36358                 html: this.title
36359             });
36360         }
36361         
36362         if(this.html.length){
36363             cn.push({
36364                 tag: 'p',
36365                 cls: 'roo-brick-text',
36366                 html: this.html
36367             });
36368         } else {
36369             cn.cls += ' hide';
36370         }
36371         
36372         if(this.bgimage.length){
36373             cfg.cn.push({
36374                 tag: 'img',
36375                 cls: 'roo-brick-image-view',
36376                 src: this.bgimage
36377             });
36378         }
36379         
36380         return cfg;
36381     },
36382     
36383     initEvents: function() 
36384     {
36385         if(this.title.length || this.html.length){
36386             this.el.on('mouseenter'  ,this.enter, this);
36387             this.el.on('mouseleave', this.leave, this);
36388         }
36389         
36390         Roo.EventManager.onWindowResize(this.resize, this); 
36391         
36392         if(this.bgimage.length){
36393             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36394             this.imageEl.on('load', this.onImageLoad, this);
36395             return;
36396         }
36397         
36398         this.resize();
36399     },
36400     
36401     onImageLoad : function()
36402     {
36403         this.resize();
36404     },
36405     
36406     resize : function()
36407     {
36408         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36409         
36410         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36411         
36412         if(this.bgimage.length){
36413             var image = this.el.select('.roo-brick-image-view', true).first();
36414             
36415             image.setWidth(paragraph.getWidth());
36416             
36417             if(this.square){
36418                 image.setHeight(paragraph.getWidth());
36419             }
36420             
36421             this.el.setHeight(image.getHeight());
36422             paragraph.setHeight(image.getHeight());
36423             
36424         }
36425         
36426     },
36427     
36428     enter: function(e, el)
36429     {
36430         e.preventDefault();
36431         
36432         if(this.bgimage.length){
36433             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36434             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36435         }
36436     },
36437     
36438     leave: function(e, el)
36439     {
36440         e.preventDefault();
36441         
36442         if(this.bgimage.length){
36443             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36444             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36445         }
36446     }
36447     
36448 });
36449
36450  
36451
36452  /*
36453  * - LGPL
36454  *
36455  * Number field 
36456  */
36457
36458 /**
36459  * @class Roo.bootstrap.NumberField
36460  * @extends Roo.bootstrap.Input
36461  * Bootstrap NumberField class
36462  * 
36463  * 
36464  * 
36465  * 
36466  * @constructor
36467  * Create a new NumberField
36468  * @param {Object} config The config object
36469  */
36470
36471 Roo.bootstrap.NumberField = function(config){
36472     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36473 };
36474
36475 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36476     
36477     /**
36478      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36479      */
36480     allowDecimals : true,
36481     /**
36482      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36483      */
36484     decimalSeparator : ".",
36485     /**
36486      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36487      */
36488     decimalPrecision : 2,
36489     /**
36490      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36491      */
36492     allowNegative : true,
36493     
36494     /**
36495      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36496      */
36497     allowZero: true,
36498     /**
36499      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36500      */
36501     minValue : Number.NEGATIVE_INFINITY,
36502     /**
36503      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36504      */
36505     maxValue : Number.MAX_VALUE,
36506     /**
36507      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36508      */
36509     minText : "The minimum value for this field is {0}",
36510     /**
36511      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36512      */
36513     maxText : "The maximum value for this field is {0}",
36514     /**
36515      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36516      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36517      */
36518     nanText : "{0} is not a valid number",
36519     /**
36520      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36521      */
36522     thousandsDelimiter : false,
36523     /**
36524      * @cfg {String} valueAlign alignment of value
36525      */
36526     valueAlign : "left",
36527
36528     getAutoCreate : function()
36529     {
36530         var hiddenInput = {
36531             tag: 'input',
36532             type: 'hidden',
36533             id: Roo.id(),
36534             cls: 'hidden-number-input'
36535         };
36536         
36537         if (this.name) {
36538             hiddenInput.name = this.name;
36539         }
36540         
36541         this.name = '';
36542         
36543         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36544         
36545         this.name = hiddenInput.name;
36546         
36547         if(cfg.cn.length > 0) {
36548             cfg.cn.push(hiddenInput);
36549         }
36550         
36551         return cfg;
36552     },
36553
36554     // private
36555     initEvents : function()
36556     {   
36557         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36558         
36559         var allowed = "0123456789";
36560         
36561         if(this.allowDecimals){
36562             allowed += this.decimalSeparator;
36563         }
36564         
36565         if(this.allowNegative){
36566             allowed += "-";
36567         }
36568         
36569         if(this.thousandsDelimiter) {
36570             allowed += ",";
36571         }
36572         
36573         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36574         
36575         var keyPress = function(e){
36576             
36577             var k = e.getKey();
36578             
36579             var c = e.getCharCode();
36580             
36581             if(
36582                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36583                     allowed.indexOf(String.fromCharCode(c)) === -1
36584             ){
36585                 e.stopEvent();
36586                 return;
36587             }
36588             
36589             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36590                 return;
36591             }
36592             
36593             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36594                 e.stopEvent();
36595             }
36596         };
36597         
36598         this.el.on("keypress", keyPress, this);
36599     },
36600     
36601     validateValue : function(value)
36602     {
36603         
36604         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36605             return false;
36606         }
36607         
36608         var num = this.parseValue(value);
36609         
36610         if(isNaN(num)){
36611             this.markInvalid(String.format(this.nanText, value));
36612             return false;
36613         }
36614         
36615         if(num < this.minValue){
36616             this.markInvalid(String.format(this.minText, this.minValue));
36617             return false;
36618         }
36619         
36620         if(num > this.maxValue){
36621             this.markInvalid(String.format(this.maxText, this.maxValue));
36622             return false;
36623         }
36624         
36625         return true;
36626     },
36627
36628     getValue : function()
36629     {
36630         var v = this.hiddenEl().getValue();
36631         
36632         return this.fixPrecision(this.parseValue(v));
36633     },
36634
36635     parseValue : function(value)
36636     {
36637         if(this.thousandsDelimiter) {
36638             value += "";
36639             r = new RegExp(",", "g");
36640             value = value.replace(r, "");
36641         }
36642         
36643         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36644         return isNaN(value) ? '' : value;
36645     },
36646
36647     fixPrecision : function(value)
36648     {
36649         if(this.thousandsDelimiter) {
36650             value += "";
36651             r = new RegExp(",", "g");
36652             value = value.replace(r, "");
36653         }
36654         
36655         var nan = isNaN(value);
36656         
36657         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36658             return nan ? '' : value;
36659         }
36660         return parseFloat(value).toFixed(this.decimalPrecision);
36661     },
36662
36663     setValue : function(v)
36664     {
36665         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36666         
36667         this.value = v;
36668         
36669         if(this.rendered){
36670             
36671             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36672             
36673             this.inputEl().dom.value = (v == '') ? '' :
36674                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36675             
36676             if(!this.allowZero && v === '0') {
36677                 this.hiddenEl().dom.value = '';
36678                 this.inputEl().dom.value = '';
36679             }
36680             
36681             this.validate();
36682         }
36683     },
36684
36685     decimalPrecisionFcn : function(v)
36686     {
36687         return Math.floor(v);
36688     },
36689
36690     beforeBlur : function()
36691     {
36692         var v = this.parseValue(this.getRawValue());
36693         
36694         if(v || v === 0 || v === ''){
36695             this.setValue(v);
36696         }
36697     },
36698     
36699     hiddenEl : function()
36700     {
36701         return this.el.select('input.hidden-number-input',true).first();
36702     }
36703     
36704 });
36705
36706  
36707
36708 /*
36709 * Licence: LGPL
36710 */
36711
36712 /**
36713  * @class Roo.bootstrap.DocumentSlider
36714  * @extends Roo.bootstrap.Component
36715  * Bootstrap DocumentSlider class
36716  * 
36717  * @constructor
36718  * Create a new DocumentViewer
36719  * @param {Object} config The config object
36720  */
36721
36722 Roo.bootstrap.DocumentSlider = function(config){
36723     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36724     
36725     this.files = [];
36726     
36727     this.addEvents({
36728         /**
36729          * @event initial
36730          * Fire after initEvent
36731          * @param {Roo.bootstrap.DocumentSlider} this
36732          */
36733         "initial" : true,
36734         /**
36735          * @event update
36736          * Fire after update
36737          * @param {Roo.bootstrap.DocumentSlider} this
36738          */
36739         "update" : true,
36740         /**
36741          * @event click
36742          * Fire after click
36743          * @param {Roo.bootstrap.DocumentSlider} this
36744          */
36745         "click" : true
36746     });
36747 };
36748
36749 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36750     
36751     files : false,
36752     
36753     indicator : 0,
36754     
36755     getAutoCreate : function()
36756     {
36757         var cfg = {
36758             tag : 'div',
36759             cls : 'roo-document-slider',
36760             cn : [
36761                 {
36762                     tag : 'div',
36763                     cls : 'roo-document-slider-header',
36764                     cn : [
36765                         {
36766                             tag : 'div',
36767                             cls : 'roo-document-slider-header-title'
36768                         }
36769                     ]
36770                 },
36771                 {
36772                     tag : 'div',
36773                     cls : 'roo-document-slider-body',
36774                     cn : [
36775                         {
36776                             tag : 'div',
36777                             cls : 'roo-document-slider-prev',
36778                             cn : [
36779                                 {
36780                                     tag : 'i',
36781                                     cls : 'fa fa-chevron-left'
36782                                 }
36783                             ]
36784                         },
36785                         {
36786                             tag : 'div',
36787                             cls : 'roo-document-slider-thumb',
36788                             cn : [
36789                                 {
36790                                     tag : 'img',
36791                                     cls : 'roo-document-slider-image'
36792                                 }
36793                             ]
36794                         },
36795                         {
36796                             tag : 'div',
36797                             cls : 'roo-document-slider-next',
36798                             cn : [
36799                                 {
36800                                     tag : 'i',
36801                                     cls : 'fa fa-chevron-right'
36802                                 }
36803                             ]
36804                         }
36805                     ]
36806                 }
36807             ]
36808         };
36809         
36810         return cfg;
36811     },
36812     
36813     initEvents : function()
36814     {
36815         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36816         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36817         
36818         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36819         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36820         
36821         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36822         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36823         
36824         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36825         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36826         
36827         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36828         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36829         
36830         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36831         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36832         
36833         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36834         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36835         
36836         this.thumbEl.on('click', this.onClick, this);
36837         
36838         this.prevIndicator.on('click', this.prev, this);
36839         
36840         this.nextIndicator.on('click', this.next, this);
36841         
36842     },
36843     
36844     initial : function()
36845     {
36846         if(this.files.length){
36847             this.indicator = 1;
36848             this.update()
36849         }
36850         
36851         this.fireEvent('initial', this);
36852     },
36853     
36854     update : function()
36855     {
36856         this.imageEl.attr('src', this.files[this.indicator - 1]);
36857         
36858         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36859         
36860         this.prevIndicator.show();
36861         
36862         if(this.indicator == 1){
36863             this.prevIndicator.hide();
36864         }
36865         
36866         this.nextIndicator.show();
36867         
36868         if(this.indicator == this.files.length){
36869             this.nextIndicator.hide();
36870         }
36871         
36872         this.thumbEl.scrollTo('top');
36873         
36874         this.fireEvent('update', this);
36875     },
36876     
36877     onClick : function(e)
36878     {
36879         e.preventDefault();
36880         
36881         this.fireEvent('click', this);
36882     },
36883     
36884     prev : function(e)
36885     {
36886         e.preventDefault();
36887         
36888         this.indicator = Math.max(1, this.indicator - 1);
36889         
36890         this.update();
36891     },
36892     
36893     next : function(e)
36894     {
36895         e.preventDefault();
36896         
36897         this.indicator = Math.min(this.files.length, this.indicator + 1);
36898         
36899         this.update();
36900     }
36901 });
36902 /*
36903  * - LGPL
36904  *
36905  * RadioSet
36906  *
36907  *
36908  */
36909
36910 /**
36911  * @class Roo.bootstrap.RadioSet
36912  * @extends Roo.bootstrap.Input
36913  * Bootstrap RadioSet class
36914  * @cfg {String} indicatorpos (left|right) default left
36915  * @cfg {Boolean} inline (true|false) inline the element (default true)
36916  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36917  * @constructor
36918  * Create a new RadioSet
36919  * @param {Object} config The config object
36920  */
36921
36922 Roo.bootstrap.RadioSet = function(config){
36923     
36924     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36925     
36926     this.radioes = [];
36927     
36928     Roo.bootstrap.RadioSet.register(this);
36929     
36930     this.addEvents({
36931         /**
36932         * @event check
36933         * Fires when the element is checked or unchecked.
36934         * @param {Roo.bootstrap.RadioSet} this This radio
36935         * @param {Roo.bootstrap.Radio} item The checked item
36936         */
36937        check : true,
36938        /**
36939         * @event click
36940         * Fires when the element is click.
36941         * @param {Roo.bootstrap.RadioSet} this This radio set
36942         * @param {Roo.bootstrap.Radio} item The checked item
36943         * @param {Roo.EventObject} e The event object
36944         */
36945        click : true
36946     });
36947     
36948 };
36949
36950 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36951
36952     radioes : false,
36953     
36954     inline : true,
36955     
36956     weight : '',
36957     
36958     indicatorpos : 'left',
36959     
36960     getAutoCreate : function()
36961     {
36962         var label = {
36963             tag : 'label',
36964             cls : 'roo-radio-set-label',
36965             cn : [
36966                 {
36967                     tag : 'span',
36968                     html : this.fieldLabel
36969                 }
36970             ]
36971         };
36972         if (Roo.bootstrap.version == 3) {
36973             
36974             
36975             if(this.indicatorpos == 'left'){
36976                 label.cn.unshift({
36977                     tag : 'i',
36978                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36979                     tooltip : 'This field is required'
36980                 });
36981             } else {
36982                 label.cn.push({
36983                     tag : 'i',
36984                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36985                     tooltip : 'This field is required'
36986                 });
36987             }
36988         }
36989         var items = {
36990             tag : 'div',
36991             cls : 'roo-radio-set-items'
36992         };
36993         
36994         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36995         
36996         if (align === 'left' && this.fieldLabel.length) {
36997             
36998             items = {
36999                 cls : "roo-radio-set-right", 
37000                 cn: [
37001                     items
37002                 ]
37003             };
37004             
37005             if(this.labelWidth > 12){
37006                 label.style = "width: " + this.labelWidth + 'px';
37007             }
37008             
37009             if(this.labelWidth < 13 && this.labelmd == 0){
37010                 this.labelmd = this.labelWidth;
37011             }
37012             
37013             if(this.labellg > 0){
37014                 label.cls += ' col-lg-' + this.labellg;
37015                 items.cls += ' col-lg-' + (12 - this.labellg);
37016             }
37017             
37018             if(this.labelmd > 0){
37019                 label.cls += ' col-md-' + this.labelmd;
37020                 items.cls += ' col-md-' + (12 - this.labelmd);
37021             }
37022             
37023             if(this.labelsm > 0){
37024                 label.cls += ' col-sm-' + this.labelsm;
37025                 items.cls += ' col-sm-' + (12 - this.labelsm);
37026             }
37027             
37028             if(this.labelxs > 0){
37029                 label.cls += ' col-xs-' + this.labelxs;
37030                 items.cls += ' col-xs-' + (12 - this.labelxs);
37031             }
37032         }
37033         
37034         var cfg = {
37035             tag : 'div',
37036             cls : 'roo-radio-set',
37037             cn : [
37038                 {
37039                     tag : 'input',
37040                     cls : 'roo-radio-set-input',
37041                     type : 'hidden',
37042                     name : this.name,
37043                     value : this.value ? this.value :  ''
37044                 },
37045                 label,
37046                 items
37047             ]
37048         };
37049         
37050         if(this.weight.length){
37051             cfg.cls += ' roo-radio-' + this.weight;
37052         }
37053         
37054         if(this.inline) {
37055             cfg.cls += ' roo-radio-set-inline';
37056         }
37057         
37058         var settings=this;
37059         ['xs','sm','md','lg'].map(function(size){
37060             if (settings[size]) {
37061                 cfg.cls += ' col-' + size + '-' + settings[size];
37062             }
37063         });
37064         
37065         return cfg;
37066         
37067     },
37068
37069     initEvents : function()
37070     {
37071         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37072         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37073         
37074         if(!this.fieldLabel.length){
37075             this.labelEl.hide();
37076         }
37077         
37078         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37079         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37080         
37081         this.indicator = this.indicatorEl();
37082         
37083         if(this.indicator){
37084             this.indicator.addClass('invisible');
37085         }
37086         
37087         this.originalValue = this.getValue();
37088         
37089     },
37090     
37091     inputEl: function ()
37092     {
37093         return this.el.select('.roo-radio-set-input', true).first();
37094     },
37095     
37096     getChildContainer : function()
37097     {
37098         return this.itemsEl;
37099     },
37100     
37101     register : function(item)
37102     {
37103         this.radioes.push(item);
37104         
37105     },
37106     
37107     validate : function()
37108     {   
37109         if(this.getVisibilityEl().hasClass('hidden')){
37110             return true;
37111         }
37112         
37113         var valid = false;
37114         
37115         Roo.each(this.radioes, function(i){
37116             if(!i.checked){
37117                 return;
37118             }
37119             
37120             valid = true;
37121             return false;
37122         });
37123         
37124         if(this.allowBlank) {
37125             return true;
37126         }
37127         
37128         if(this.disabled || valid){
37129             this.markValid();
37130             return true;
37131         }
37132         
37133         this.markInvalid();
37134         return false;
37135         
37136     },
37137     
37138     markValid : function()
37139     {
37140         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37141             this.indicatorEl().removeClass('visible');
37142             this.indicatorEl().addClass('invisible');
37143         }
37144         
37145         
37146         if (Roo.bootstrap.version == 3) {
37147             this.el.removeClass([this.invalidClass, this.validClass]);
37148             this.el.addClass(this.validClass);
37149         } else {
37150             this.el.removeClass(['is-invalid','is-valid']);
37151             this.el.addClass(['is-valid']);
37152         }
37153         this.fireEvent('valid', this);
37154     },
37155     
37156     markInvalid : function(msg)
37157     {
37158         if(this.allowBlank || this.disabled){
37159             return;
37160         }
37161         
37162         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37163             this.indicatorEl().removeClass('invisible');
37164             this.indicatorEl().addClass('visible');
37165         }
37166         if (Roo.bootstrap.version == 3) {
37167             this.el.removeClass([this.invalidClass, this.validClass]);
37168             this.el.addClass(this.invalidClass);
37169         } else {
37170             this.el.removeClass(['is-invalid','is-valid']);
37171             this.el.addClass(['is-invalid']);
37172         }
37173         
37174         this.fireEvent('invalid', this, msg);
37175         
37176     },
37177     
37178     setValue : function(v, suppressEvent)
37179     {   
37180         if(this.value === v){
37181             return;
37182         }
37183         
37184         this.value = v;
37185         
37186         if(this.rendered){
37187             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37188         }
37189         
37190         Roo.each(this.radioes, function(i){
37191             i.checked = false;
37192             i.el.removeClass('checked');
37193         });
37194         
37195         Roo.each(this.radioes, function(i){
37196             
37197             if(i.value === v || i.value.toString() === v.toString()){
37198                 i.checked = true;
37199                 i.el.addClass('checked');
37200                 
37201                 if(suppressEvent !== true){
37202                     this.fireEvent('check', this, i);
37203                 }
37204                 
37205                 return false;
37206             }
37207             
37208         }, this);
37209         
37210         this.validate();
37211     },
37212     
37213     clearInvalid : function(){
37214         
37215         if(!this.el || this.preventMark){
37216             return;
37217         }
37218         
37219         this.el.removeClass([this.invalidClass]);
37220         
37221         this.fireEvent('valid', this);
37222     }
37223     
37224 });
37225
37226 Roo.apply(Roo.bootstrap.RadioSet, {
37227     
37228     groups: {},
37229     
37230     register : function(set)
37231     {
37232         this.groups[set.name] = set;
37233     },
37234     
37235     get: function(name) 
37236     {
37237         if (typeof(this.groups[name]) == 'undefined') {
37238             return false;
37239         }
37240         
37241         return this.groups[name] ;
37242     }
37243     
37244 });
37245 /*
37246  * Based on:
37247  * Ext JS Library 1.1.1
37248  * Copyright(c) 2006-2007, Ext JS, LLC.
37249  *
37250  * Originally Released Under LGPL - original licence link has changed is not relivant.
37251  *
37252  * Fork - LGPL
37253  * <script type="text/javascript">
37254  */
37255
37256
37257 /**
37258  * @class Roo.bootstrap.SplitBar
37259  * @extends Roo.util.Observable
37260  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37261  * <br><br>
37262  * Usage:
37263  * <pre><code>
37264 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37265                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37266 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37267 split.minSize = 100;
37268 split.maxSize = 600;
37269 split.animate = true;
37270 split.on('moved', splitterMoved);
37271 </code></pre>
37272  * @constructor
37273  * Create a new SplitBar
37274  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37275  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37276  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37277  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37278                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37279                         position of the SplitBar).
37280  */
37281 Roo.bootstrap.SplitBar = function(cfg){
37282     
37283     /** @private */
37284     
37285     //{
37286     //  dragElement : elm
37287     //  resizingElement: el,
37288         // optional..
37289     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37290     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37291         // existingProxy ???
37292     //}
37293     
37294     this.el = Roo.get(cfg.dragElement, true);
37295     this.el.dom.unselectable = "on";
37296     /** @private */
37297     this.resizingEl = Roo.get(cfg.resizingElement, true);
37298
37299     /**
37300      * @private
37301      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37302      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37303      * @type Number
37304      */
37305     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37306     
37307     /**
37308      * The minimum size of the resizing element. (Defaults to 0)
37309      * @type Number
37310      */
37311     this.minSize = 0;
37312     
37313     /**
37314      * The maximum size of the resizing element. (Defaults to 2000)
37315      * @type Number
37316      */
37317     this.maxSize = 2000;
37318     
37319     /**
37320      * Whether to animate the transition to the new size
37321      * @type Boolean
37322      */
37323     this.animate = false;
37324     
37325     /**
37326      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37327      * @type Boolean
37328      */
37329     this.useShim = false;
37330     
37331     /** @private */
37332     this.shim = null;
37333     
37334     if(!cfg.existingProxy){
37335         /** @private */
37336         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37337     }else{
37338         this.proxy = Roo.get(cfg.existingProxy).dom;
37339     }
37340     /** @private */
37341     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37342     
37343     /** @private */
37344     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37345     
37346     /** @private */
37347     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37348     
37349     /** @private */
37350     this.dragSpecs = {};
37351     
37352     /**
37353      * @private The adapter to use to positon and resize elements
37354      */
37355     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37356     this.adapter.init(this);
37357     
37358     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37359         /** @private */
37360         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37361         this.el.addClass("roo-splitbar-h");
37362     }else{
37363         /** @private */
37364         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37365         this.el.addClass("roo-splitbar-v");
37366     }
37367     
37368     this.addEvents({
37369         /**
37370          * @event resize
37371          * Fires when the splitter is moved (alias for {@link #event-moved})
37372          * @param {Roo.bootstrap.SplitBar} this
37373          * @param {Number} newSize the new width or height
37374          */
37375         "resize" : true,
37376         /**
37377          * @event moved
37378          * Fires when the splitter is moved
37379          * @param {Roo.bootstrap.SplitBar} this
37380          * @param {Number} newSize the new width or height
37381          */
37382         "moved" : true,
37383         /**
37384          * @event beforeresize
37385          * Fires before the splitter is dragged
37386          * @param {Roo.bootstrap.SplitBar} this
37387          */
37388         "beforeresize" : true,
37389
37390         "beforeapply" : true
37391     });
37392
37393     Roo.util.Observable.call(this);
37394 };
37395
37396 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37397     onStartProxyDrag : function(x, y){
37398         this.fireEvent("beforeresize", this);
37399         if(!this.overlay){
37400             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37401             o.unselectable();
37402             o.enableDisplayMode("block");
37403             // all splitbars share the same overlay
37404             Roo.bootstrap.SplitBar.prototype.overlay = o;
37405         }
37406         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37407         this.overlay.show();
37408         Roo.get(this.proxy).setDisplayed("block");
37409         var size = this.adapter.getElementSize(this);
37410         this.activeMinSize = this.getMinimumSize();;
37411         this.activeMaxSize = this.getMaximumSize();;
37412         var c1 = size - this.activeMinSize;
37413         var c2 = Math.max(this.activeMaxSize - size, 0);
37414         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37415             this.dd.resetConstraints();
37416             this.dd.setXConstraint(
37417                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37418                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37419             );
37420             this.dd.setYConstraint(0, 0);
37421         }else{
37422             this.dd.resetConstraints();
37423             this.dd.setXConstraint(0, 0);
37424             this.dd.setYConstraint(
37425                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37426                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37427             );
37428          }
37429         this.dragSpecs.startSize = size;
37430         this.dragSpecs.startPoint = [x, y];
37431         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37432     },
37433     
37434     /** 
37435      * @private Called after the drag operation by the DDProxy
37436      */
37437     onEndProxyDrag : function(e){
37438         Roo.get(this.proxy).setDisplayed(false);
37439         var endPoint = Roo.lib.Event.getXY(e);
37440         if(this.overlay){
37441             this.overlay.hide();
37442         }
37443         var newSize;
37444         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37445             newSize = this.dragSpecs.startSize + 
37446                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37447                     endPoint[0] - this.dragSpecs.startPoint[0] :
37448                     this.dragSpecs.startPoint[0] - endPoint[0]
37449                 );
37450         }else{
37451             newSize = this.dragSpecs.startSize + 
37452                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37453                     endPoint[1] - this.dragSpecs.startPoint[1] :
37454                     this.dragSpecs.startPoint[1] - endPoint[1]
37455                 );
37456         }
37457         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37458         if(newSize != this.dragSpecs.startSize){
37459             if(this.fireEvent('beforeapply', this, newSize) !== false){
37460                 this.adapter.setElementSize(this, newSize);
37461                 this.fireEvent("moved", this, newSize);
37462                 this.fireEvent("resize", this, newSize);
37463             }
37464         }
37465     },
37466     
37467     /**
37468      * Get the adapter this SplitBar uses
37469      * @return The adapter object
37470      */
37471     getAdapter : function(){
37472         return this.adapter;
37473     },
37474     
37475     /**
37476      * Set the adapter this SplitBar uses
37477      * @param {Object} adapter A SplitBar adapter object
37478      */
37479     setAdapter : function(adapter){
37480         this.adapter = adapter;
37481         this.adapter.init(this);
37482     },
37483     
37484     /**
37485      * Gets the minimum size for the resizing element
37486      * @return {Number} The minimum size
37487      */
37488     getMinimumSize : function(){
37489         return this.minSize;
37490     },
37491     
37492     /**
37493      * Sets the minimum size for the resizing element
37494      * @param {Number} minSize The minimum size
37495      */
37496     setMinimumSize : function(minSize){
37497         this.minSize = minSize;
37498     },
37499     
37500     /**
37501      * Gets the maximum size for the resizing element
37502      * @return {Number} The maximum size
37503      */
37504     getMaximumSize : function(){
37505         return this.maxSize;
37506     },
37507     
37508     /**
37509      * Sets the maximum size for the resizing element
37510      * @param {Number} maxSize The maximum size
37511      */
37512     setMaximumSize : function(maxSize){
37513         this.maxSize = maxSize;
37514     },
37515     
37516     /**
37517      * Sets the initialize size for the resizing element
37518      * @param {Number} size The initial size
37519      */
37520     setCurrentSize : function(size){
37521         var oldAnimate = this.animate;
37522         this.animate = false;
37523         this.adapter.setElementSize(this, size);
37524         this.animate = oldAnimate;
37525     },
37526     
37527     /**
37528      * Destroy this splitbar. 
37529      * @param {Boolean} removeEl True to remove the element
37530      */
37531     destroy : function(removeEl){
37532         if(this.shim){
37533             this.shim.remove();
37534         }
37535         this.dd.unreg();
37536         this.proxy.parentNode.removeChild(this.proxy);
37537         if(removeEl){
37538             this.el.remove();
37539         }
37540     }
37541 });
37542
37543 /**
37544  * @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.
37545  */
37546 Roo.bootstrap.SplitBar.createProxy = function(dir){
37547     var proxy = new Roo.Element(document.createElement("div"));
37548     proxy.unselectable();
37549     var cls = 'roo-splitbar-proxy';
37550     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37551     document.body.appendChild(proxy.dom);
37552     return proxy.dom;
37553 };
37554
37555 /** 
37556  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37557  * Default Adapter. It assumes the splitter and resizing element are not positioned
37558  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37559  */
37560 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37561 };
37562
37563 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37564     // do nothing for now
37565     init : function(s){
37566     
37567     },
37568     /**
37569      * Called before drag operations to get the current size of the resizing element. 
37570      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37571      */
37572      getElementSize : function(s){
37573         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37574             return s.resizingEl.getWidth();
37575         }else{
37576             return s.resizingEl.getHeight();
37577         }
37578     },
37579     
37580     /**
37581      * Called after drag operations to set the size of the resizing element.
37582      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37583      * @param {Number} newSize The new size to set
37584      * @param {Function} onComplete A function to be invoked when resizing is complete
37585      */
37586     setElementSize : function(s, newSize, onComplete){
37587         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37588             if(!s.animate){
37589                 s.resizingEl.setWidth(newSize);
37590                 if(onComplete){
37591                     onComplete(s, newSize);
37592                 }
37593             }else{
37594                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37595             }
37596         }else{
37597             
37598             if(!s.animate){
37599                 s.resizingEl.setHeight(newSize);
37600                 if(onComplete){
37601                     onComplete(s, newSize);
37602                 }
37603             }else{
37604                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37605             }
37606         }
37607     }
37608 };
37609
37610 /** 
37611  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37612  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37613  * Adapter that  moves the splitter element to align with the resized sizing element. 
37614  * Used with an absolute positioned SplitBar.
37615  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37616  * document.body, make sure you assign an id to the body element.
37617  */
37618 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37619     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37620     this.container = Roo.get(container);
37621 };
37622
37623 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37624     init : function(s){
37625         this.basic.init(s);
37626     },
37627     
37628     getElementSize : function(s){
37629         return this.basic.getElementSize(s);
37630     },
37631     
37632     setElementSize : function(s, newSize, onComplete){
37633         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37634     },
37635     
37636     moveSplitter : function(s){
37637         var yes = Roo.bootstrap.SplitBar;
37638         switch(s.placement){
37639             case yes.LEFT:
37640                 s.el.setX(s.resizingEl.getRight());
37641                 break;
37642             case yes.RIGHT:
37643                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37644                 break;
37645             case yes.TOP:
37646                 s.el.setY(s.resizingEl.getBottom());
37647                 break;
37648             case yes.BOTTOM:
37649                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37650                 break;
37651         }
37652     }
37653 };
37654
37655 /**
37656  * Orientation constant - Create a vertical SplitBar
37657  * @static
37658  * @type Number
37659  */
37660 Roo.bootstrap.SplitBar.VERTICAL = 1;
37661
37662 /**
37663  * Orientation constant - Create a horizontal SplitBar
37664  * @static
37665  * @type Number
37666  */
37667 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37668
37669 /**
37670  * Placement constant - The resizing element is to the left of the splitter element
37671  * @static
37672  * @type Number
37673  */
37674 Roo.bootstrap.SplitBar.LEFT = 1;
37675
37676 /**
37677  * Placement constant - The resizing element is to the right of the splitter element
37678  * @static
37679  * @type Number
37680  */
37681 Roo.bootstrap.SplitBar.RIGHT = 2;
37682
37683 /**
37684  * Placement constant - The resizing element is positioned above the splitter element
37685  * @static
37686  * @type Number
37687  */
37688 Roo.bootstrap.SplitBar.TOP = 3;
37689
37690 /**
37691  * Placement constant - The resizing element is positioned under splitter element
37692  * @static
37693  * @type Number
37694  */
37695 Roo.bootstrap.SplitBar.BOTTOM = 4;
37696 Roo.namespace("Roo.bootstrap.layout");/*
37697  * Based on:
37698  * Ext JS Library 1.1.1
37699  * Copyright(c) 2006-2007, Ext JS, LLC.
37700  *
37701  * Originally Released Under LGPL - original licence link has changed is not relivant.
37702  *
37703  * Fork - LGPL
37704  * <script type="text/javascript">
37705  */
37706
37707 /**
37708  * @class Roo.bootstrap.layout.Manager
37709  * @extends Roo.bootstrap.Component
37710  * Base class for layout managers.
37711  */
37712 Roo.bootstrap.layout.Manager = function(config)
37713 {
37714     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37715
37716
37717
37718
37719
37720     /** false to disable window resize monitoring @type Boolean */
37721     this.monitorWindowResize = true;
37722     this.regions = {};
37723     this.addEvents({
37724         /**
37725          * @event layout
37726          * Fires when a layout is performed.
37727          * @param {Roo.LayoutManager} this
37728          */
37729         "layout" : true,
37730         /**
37731          * @event regionresized
37732          * Fires when the user resizes a region.
37733          * @param {Roo.LayoutRegion} region The resized region
37734          * @param {Number} newSize The new size (width for east/west, height for north/south)
37735          */
37736         "regionresized" : true,
37737         /**
37738          * @event regioncollapsed
37739          * Fires when a region is collapsed.
37740          * @param {Roo.LayoutRegion} region The collapsed region
37741          */
37742         "regioncollapsed" : true,
37743         /**
37744          * @event regionexpanded
37745          * Fires when a region is expanded.
37746          * @param {Roo.LayoutRegion} region The expanded region
37747          */
37748         "regionexpanded" : true
37749     });
37750     this.updating = false;
37751
37752     if (config.el) {
37753         this.el = Roo.get(config.el);
37754         this.initEvents();
37755     }
37756
37757 };
37758
37759 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37760
37761
37762     regions : null,
37763
37764     monitorWindowResize : true,
37765
37766
37767     updating : false,
37768
37769
37770     onRender : function(ct, position)
37771     {
37772         if(!this.el){
37773             this.el = Roo.get(ct);
37774             this.initEvents();
37775         }
37776         //this.fireEvent('render',this);
37777     },
37778
37779
37780     initEvents: function()
37781     {
37782
37783
37784         // ie scrollbar fix
37785         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37786             document.body.scroll = "no";
37787         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37788             this.el.position('relative');
37789         }
37790         this.id = this.el.id;
37791         this.el.addClass("roo-layout-container");
37792         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37793         if(this.el.dom != document.body ) {
37794             this.el.on('resize', this.layout,this);
37795             this.el.on('show', this.layout,this);
37796         }
37797
37798     },
37799
37800     /**
37801      * Returns true if this layout is currently being updated
37802      * @return {Boolean}
37803      */
37804     isUpdating : function(){
37805         return this.updating;
37806     },
37807
37808     /**
37809      * Suspend the LayoutManager from doing auto-layouts while
37810      * making multiple add or remove calls
37811      */
37812     beginUpdate : function(){
37813         this.updating = true;
37814     },
37815
37816     /**
37817      * Restore auto-layouts and optionally disable the manager from performing a layout
37818      * @param {Boolean} noLayout true to disable a layout update
37819      */
37820     endUpdate : function(noLayout){
37821         this.updating = false;
37822         if(!noLayout){
37823             this.layout();
37824         }
37825     },
37826
37827     layout: function(){
37828         // abstract...
37829     },
37830
37831     onRegionResized : function(region, newSize){
37832         this.fireEvent("regionresized", region, newSize);
37833         this.layout();
37834     },
37835
37836     onRegionCollapsed : function(region){
37837         this.fireEvent("regioncollapsed", region);
37838     },
37839
37840     onRegionExpanded : function(region){
37841         this.fireEvent("regionexpanded", region);
37842     },
37843
37844     /**
37845      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37846      * performs box-model adjustments.
37847      * @return {Object} The size as an object {width: (the width), height: (the height)}
37848      */
37849     getViewSize : function()
37850     {
37851         var size;
37852         if(this.el.dom != document.body){
37853             size = this.el.getSize();
37854         }else{
37855             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37856         }
37857         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37858         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37859         return size;
37860     },
37861
37862     /**
37863      * Returns the Element this layout is bound to.
37864      * @return {Roo.Element}
37865      */
37866     getEl : function(){
37867         return this.el;
37868     },
37869
37870     /**
37871      * Returns the specified region.
37872      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37873      * @return {Roo.LayoutRegion}
37874      */
37875     getRegion : function(target){
37876         return this.regions[target.toLowerCase()];
37877     },
37878
37879     onWindowResize : function(){
37880         if(this.monitorWindowResize){
37881             this.layout();
37882         }
37883     }
37884 });
37885 /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895 /**
37896  * @class Roo.bootstrap.layout.Border
37897  * @extends Roo.bootstrap.layout.Manager
37898  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37899  * please see: examples/bootstrap/nested.html<br><br>
37900  
37901 <b>The container the layout is rendered into can be either the body element or any other element.
37902 If it is not the body element, the container needs to either be an absolute positioned element,
37903 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37904 the container size if it is not the body element.</b>
37905
37906 * @constructor
37907 * Create a new Border
37908 * @param {Object} config Configuration options
37909  */
37910 Roo.bootstrap.layout.Border = function(config){
37911     config = config || {};
37912     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37913     
37914     
37915     
37916     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37917         if(config[region]){
37918             config[region].region = region;
37919             this.addRegion(config[region]);
37920         }
37921     },this);
37922     
37923 };
37924
37925 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37926
37927 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37928     
37929     parent : false, // this might point to a 'nest' or a ???
37930     
37931     /**
37932      * Creates and adds a new region if it doesn't already exist.
37933      * @param {String} target The target region key (north, south, east, west or center).
37934      * @param {Object} config The regions config object
37935      * @return {BorderLayoutRegion} The new region
37936      */
37937     addRegion : function(config)
37938     {
37939         if(!this.regions[config.region]){
37940             var r = this.factory(config);
37941             this.bindRegion(r);
37942         }
37943         return this.regions[config.region];
37944     },
37945
37946     // private (kinda)
37947     bindRegion : function(r){
37948         this.regions[r.config.region] = r;
37949         
37950         r.on("visibilitychange",    this.layout, this);
37951         r.on("paneladded",          this.layout, this);
37952         r.on("panelremoved",        this.layout, this);
37953         r.on("invalidated",         this.layout, this);
37954         r.on("resized",             this.onRegionResized, this);
37955         r.on("collapsed",           this.onRegionCollapsed, this);
37956         r.on("expanded",            this.onRegionExpanded, this);
37957     },
37958
37959     /**
37960      * Performs a layout update.
37961      */
37962     layout : function()
37963     {
37964         if(this.updating) {
37965             return;
37966         }
37967         
37968         // render all the rebions if they have not been done alreayd?
37969         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37970             if(this.regions[region] && !this.regions[region].bodyEl){
37971                 this.regions[region].onRender(this.el)
37972             }
37973         },this);
37974         
37975         var size = this.getViewSize();
37976         var w = size.width;
37977         var h = size.height;
37978         var centerW = w;
37979         var centerH = h;
37980         var centerY = 0;
37981         var centerX = 0;
37982         //var x = 0, y = 0;
37983
37984         var rs = this.regions;
37985         var north = rs["north"];
37986         var south = rs["south"]; 
37987         var west = rs["west"];
37988         var east = rs["east"];
37989         var center = rs["center"];
37990         //if(this.hideOnLayout){ // not supported anymore
37991             //c.el.setStyle("display", "none");
37992         //}
37993         if(north && north.isVisible()){
37994             var b = north.getBox();
37995             var m = north.getMargins();
37996             b.width = w - (m.left+m.right);
37997             b.x = m.left;
37998             b.y = m.top;
37999             centerY = b.height + b.y + m.bottom;
38000             centerH -= centerY;
38001             north.updateBox(this.safeBox(b));
38002         }
38003         if(south && south.isVisible()){
38004             var b = south.getBox();
38005             var m = south.getMargins();
38006             b.width = w - (m.left+m.right);
38007             b.x = m.left;
38008             var totalHeight = (b.height + m.top + m.bottom);
38009             b.y = h - totalHeight + m.top;
38010             centerH -= totalHeight;
38011             south.updateBox(this.safeBox(b));
38012         }
38013         if(west && west.isVisible()){
38014             var b = west.getBox();
38015             var m = west.getMargins();
38016             b.height = centerH - (m.top+m.bottom);
38017             b.x = m.left;
38018             b.y = centerY + m.top;
38019             var totalWidth = (b.width + m.left + m.right);
38020             centerX += totalWidth;
38021             centerW -= totalWidth;
38022             west.updateBox(this.safeBox(b));
38023         }
38024         if(east && east.isVisible()){
38025             var b = east.getBox();
38026             var m = east.getMargins();
38027             b.height = centerH - (m.top+m.bottom);
38028             var totalWidth = (b.width + m.left + m.right);
38029             b.x = w - totalWidth + m.left;
38030             b.y = centerY + m.top;
38031             centerW -= totalWidth;
38032             east.updateBox(this.safeBox(b));
38033         }
38034         if(center){
38035             var m = center.getMargins();
38036             var centerBox = {
38037                 x: centerX + m.left,
38038                 y: centerY + m.top,
38039                 width: centerW - (m.left+m.right),
38040                 height: centerH - (m.top+m.bottom)
38041             };
38042             //if(this.hideOnLayout){
38043                 //center.el.setStyle("display", "block");
38044             //}
38045             center.updateBox(this.safeBox(centerBox));
38046         }
38047         this.el.repaint();
38048         this.fireEvent("layout", this);
38049     },
38050
38051     // private
38052     safeBox : function(box){
38053         box.width = Math.max(0, box.width);
38054         box.height = Math.max(0, box.height);
38055         return box;
38056     },
38057
38058     /**
38059      * Adds a ContentPanel (or subclass) to this layout.
38060      * @param {String} target The target region key (north, south, east, west or center).
38061      * @param {Roo.ContentPanel} panel The panel to add
38062      * @return {Roo.ContentPanel} The added panel
38063      */
38064     add : function(target, panel){
38065          
38066         target = target.toLowerCase();
38067         return this.regions[target].add(panel);
38068     },
38069
38070     /**
38071      * Remove a ContentPanel (or subclass) to this layout.
38072      * @param {String} target The target region key (north, south, east, west or center).
38073      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38074      * @return {Roo.ContentPanel} The removed panel
38075      */
38076     remove : function(target, panel){
38077         target = target.toLowerCase();
38078         return this.regions[target].remove(panel);
38079     },
38080
38081     /**
38082      * Searches all regions for a panel with the specified id
38083      * @param {String} panelId
38084      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38085      */
38086     findPanel : function(panelId){
38087         var rs = this.regions;
38088         for(var target in rs){
38089             if(typeof rs[target] != "function"){
38090                 var p = rs[target].getPanel(panelId);
38091                 if(p){
38092                     return p;
38093                 }
38094             }
38095         }
38096         return null;
38097     },
38098
38099     /**
38100      * Searches all regions for a panel with the specified id and activates (shows) it.
38101      * @param {String/ContentPanel} panelId The panels id or the panel itself
38102      * @return {Roo.ContentPanel} The shown panel or null
38103      */
38104     showPanel : function(panelId) {
38105       var rs = this.regions;
38106       for(var target in rs){
38107          var r = rs[target];
38108          if(typeof r != "function"){
38109             if(r.hasPanel(panelId)){
38110                return r.showPanel(panelId);
38111             }
38112          }
38113       }
38114       return null;
38115    },
38116
38117    /**
38118      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38119      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38120      */
38121    /*
38122     restoreState : function(provider){
38123         if(!provider){
38124             provider = Roo.state.Manager;
38125         }
38126         var sm = new Roo.LayoutStateManager();
38127         sm.init(this, provider);
38128     },
38129 */
38130  
38131  
38132     /**
38133      * Adds a xtype elements to the layout.
38134      * <pre><code>
38135
38136 layout.addxtype({
38137        xtype : 'ContentPanel',
38138        region: 'west',
38139        items: [ .... ]
38140    }
38141 );
38142
38143 layout.addxtype({
38144         xtype : 'NestedLayoutPanel',
38145         region: 'west',
38146         layout: {
38147            center: { },
38148            west: { }   
38149         },
38150         items : [ ... list of content panels or nested layout panels.. ]
38151    }
38152 );
38153 </code></pre>
38154      * @param {Object} cfg Xtype definition of item to add.
38155      */
38156     addxtype : function(cfg)
38157     {
38158         // basically accepts a pannel...
38159         // can accept a layout region..!?!?
38160         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38161         
38162         
38163         // theory?  children can only be panels??
38164         
38165         //if (!cfg.xtype.match(/Panel$/)) {
38166         //    return false;
38167         //}
38168         var ret = false;
38169         
38170         if (typeof(cfg.region) == 'undefined') {
38171             Roo.log("Failed to add Panel, region was not set");
38172             Roo.log(cfg);
38173             return false;
38174         }
38175         var region = cfg.region;
38176         delete cfg.region;
38177         
38178           
38179         var xitems = [];
38180         if (cfg.items) {
38181             xitems = cfg.items;
38182             delete cfg.items;
38183         }
38184         var nb = false;
38185         
38186         if ( region == 'center') {
38187             Roo.log("Center: " + cfg.title);
38188         }
38189         
38190         
38191         switch(cfg.xtype) 
38192         {
38193             case 'Content':  // ContentPanel (el, cfg)
38194             case 'Scroll':  // ContentPanel (el, cfg)
38195             case 'View': 
38196                 cfg.autoCreate = cfg.autoCreate || true;
38197                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38198                 //} else {
38199                 //    var el = this.el.createChild();
38200                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38201                 //}
38202                 
38203                 this.add(region, ret);
38204                 break;
38205             
38206             /*
38207             case 'TreePanel': // our new panel!
38208                 cfg.el = this.el.createChild();
38209                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38210                 this.add(region, ret);
38211                 break;
38212             */
38213             
38214             case 'Nest': 
38215                 // create a new Layout (which is  a Border Layout...
38216                 
38217                 var clayout = cfg.layout;
38218                 clayout.el  = this.el.createChild();
38219                 clayout.items   = clayout.items  || [];
38220                 
38221                 delete cfg.layout;
38222                 
38223                 // replace this exitems with the clayout ones..
38224                 xitems = clayout.items;
38225                  
38226                 // force background off if it's in center...
38227                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38228                     cfg.background = false;
38229                 }
38230                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38231                 
38232                 
38233                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38234                 //console.log('adding nested layout panel '  + cfg.toSource());
38235                 this.add(region, ret);
38236                 nb = {}; /// find first...
38237                 break;
38238             
38239             case 'Grid':
38240                 
38241                 // needs grid and region
38242                 
38243                 //var el = this.getRegion(region).el.createChild();
38244                 /*
38245                  *var el = this.el.createChild();
38246                 // create the grid first...
38247                 cfg.grid.container = el;
38248                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38249                 */
38250                 
38251                 if (region == 'center' && this.active ) {
38252                     cfg.background = false;
38253                 }
38254                 
38255                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38256                 
38257                 this.add(region, ret);
38258                 /*
38259                 if (cfg.background) {
38260                     // render grid on panel activation (if panel background)
38261                     ret.on('activate', function(gp) {
38262                         if (!gp.grid.rendered) {
38263                     //        gp.grid.render(el);
38264                         }
38265                     });
38266                 } else {
38267                   //  cfg.grid.render(el);
38268                 }
38269                 */
38270                 break;
38271            
38272            
38273             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38274                 // it was the old xcomponent building that caused this before.
38275                 // espeically if border is the top element in the tree.
38276                 ret = this;
38277                 break; 
38278                 
38279                     
38280                 
38281                 
38282                 
38283             default:
38284                 /*
38285                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38286                     
38287                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38288                     this.add(region, ret);
38289                 } else {
38290                 */
38291                     Roo.log(cfg);
38292                     throw "Can not add '" + cfg.xtype + "' to Border";
38293                     return null;
38294              
38295                                 
38296              
38297         }
38298         this.beginUpdate();
38299         // add children..
38300         var region = '';
38301         var abn = {};
38302         Roo.each(xitems, function(i)  {
38303             region = nb && i.region ? i.region : false;
38304             
38305             var add = ret.addxtype(i);
38306            
38307             if (region) {
38308                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38309                 if (!i.background) {
38310                     abn[region] = nb[region] ;
38311                 }
38312             }
38313             
38314         });
38315         this.endUpdate();
38316
38317         // make the last non-background panel active..
38318         //if (nb) { Roo.log(abn); }
38319         if (nb) {
38320             
38321             for(var r in abn) {
38322                 region = this.getRegion(r);
38323                 if (region) {
38324                     // tried using nb[r], but it does not work..
38325                      
38326                     region.showPanel(abn[r]);
38327                    
38328                 }
38329             }
38330         }
38331         return ret;
38332         
38333     },
38334     
38335     
38336 // private
38337     factory : function(cfg)
38338     {
38339         
38340         var validRegions = Roo.bootstrap.layout.Border.regions;
38341
38342         var target = cfg.region;
38343         cfg.mgr = this;
38344         
38345         var r = Roo.bootstrap.layout;
38346         Roo.log(target);
38347         switch(target){
38348             case "north":
38349                 return new r.North(cfg);
38350             case "south":
38351                 return new r.South(cfg);
38352             case "east":
38353                 return new r.East(cfg);
38354             case "west":
38355                 return new r.West(cfg);
38356             case "center":
38357                 return new r.Center(cfg);
38358         }
38359         throw 'Layout region "'+target+'" not supported.';
38360     }
38361     
38362     
38363 });
38364  /*
38365  * Based on:
38366  * Ext JS Library 1.1.1
38367  * Copyright(c) 2006-2007, Ext JS, LLC.
38368  *
38369  * Originally Released Under LGPL - original licence link has changed is not relivant.
38370  *
38371  * Fork - LGPL
38372  * <script type="text/javascript">
38373  */
38374  
38375 /**
38376  * @class Roo.bootstrap.layout.Basic
38377  * @extends Roo.util.Observable
38378  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38379  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38380  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38381  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38382  * @cfg {string}   region  the region that it inhabits..
38383  * @cfg {bool}   skipConfig skip config?
38384  * 
38385
38386  */
38387 Roo.bootstrap.layout.Basic = function(config){
38388     
38389     this.mgr = config.mgr;
38390     
38391     this.position = config.region;
38392     
38393     var skipConfig = config.skipConfig;
38394     
38395     this.events = {
38396         /**
38397          * @scope Roo.BasicLayoutRegion
38398          */
38399         
38400         /**
38401          * @event beforeremove
38402          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38403          * @param {Roo.LayoutRegion} this
38404          * @param {Roo.ContentPanel} panel The panel
38405          * @param {Object} e The cancel event object
38406          */
38407         "beforeremove" : true,
38408         /**
38409          * @event invalidated
38410          * Fires when the layout for this region is changed.
38411          * @param {Roo.LayoutRegion} this
38412          */
38413         "invalidated" : true,
38414         /**
38415          * @event visibilitychange
38416          * Fires when this region is shown or hidden 
38417          * @param {Roo.LayoutRegion} this
38418          * @param {Boolean} visibility true or false
38419          */
38420         "visibilitychange" : true,
38421         /**
38422          * @event paneladded
38423          * Fires when a panel is added. 
38424          * @param {Roo.LayoutRegion} this
38425          * @param {Roo.ContentPanel} panel The panel
38426          */
38427         "paneladded" : true,
38428         /**
38429          * @event panelremoved
38430          * Fires when a panel is removed. 
38431          * @param {Roo.LayoutRegion} this
38432          * @param {Roo.ContentPanel} panel The panel
38433          */
38434         "panelremoved" : true,
38435         /**
38436          * @event beforecollapse
38437          * Fires when this region before collapse.
38438          * @param {Roo.LayoutRegion} this
38439          */
38440         "beforecollapse" : true,
38441         /**
38442          * @event collapsed
38443          * Fires when this region is collapsed.
38444          * @param {Roo.LayoutRegion} this
38445          */
38446         "collapsed" : true,
38447         /**
38448          * @event expanded
38449          * Fires when this region is expanded.
38450          * @param {Roo.LayoutRegion} this
38451          */
38452         "expanded" : true,
38453         /**
38454          * @event slideshow
38455          * Fires when this region is slid into view.
38456          * @param {Roo.LayoutRegion} this
38457          */
38458         "slideshow" : true,
38459         /**
38460          * @event slidehide
38461          * Fires when this region slides out of view. 
38462          * @param {Roo.LayoutRegion} this
38463          */
38464         "slidehide" : true,
38465         /**
38466          * @event panelactivated
38467          * Fires when a panel is activated. 
38468          * @param {Roo.LayoutRegion} this
38469          * @param {Roo.ContentPanel} panel The activated panel
38470          */
38471         "panelactivated" : true,
38472         /**
38473          * @event resized
38474          * Fires when the user resizes this region. 
38475          * @param {Roo.LayoutRegion} this
38476          * @param {Number} newSize The new size (width for east/west, height for north/south)
38477          */
38478         "resized" : true
38479     };
38480     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38481     this.panels = new Roo.util.MixedCollection();
38482     this.panels.getKey = this.getPanelId.createDelegate(this);
38483     this.box = null;
38484     this.activePanel = null;
38485     // ensure listeners are added...
38486     
38487     if (config.listeners || config.events) {
38488         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38489             listeners : config.listeners || {},
38490             events : config.events || {}
38491         });
38492     }
38493     
38494     if(skipConfig !== true){
38495         this.applyConfig(config);
38496     }
38497 };
38498
38499 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38500 {
38501     getPanelId : function(p){
38502         return p.getId();
38503     },
38504     
38505     applyConfig : function(config){
38506         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38507         this.config = config;
38508         
38509     },
38510     
38511     /**
38512      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38513      * the width, for horizontal (north, south) the height.
38514      * @param {Number} newSize The new width or height
38515      */
38516     resizeTo : function(newSize){
38517         var el = this.el ? this.el :
38518                  (this.activePanel ? this.activePanel.getEl() : null);
38519         if(el){
38520             switch(this.position){
38521                 case "east":
38522                 case "west":
38523                     el.setWidth(newSize);
38524                     this.fireEvent("resized", this, newSize);
38525                 break;
38526                 case "north":
38527                 case "south":
38528                     el.setHeight(newSize);
38529                     this.fireEvent("resized", this, newSize);
38530                 break;                
38531             }
38532         }
38533     },
38534     
38535     getBox : function(){
38536         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38537     },
38538     
38539     getMargins : function(){
38540         return this.margins;
38541     },
38542     
38543     updateBox : function(box){
38544         this.box = box;
38545         var el = this.activePanel.getEl();
38546         el.dom.style.left = box.x + "px";
38547         el.dom.style.top = box.y + "px";
38548         this.activePanel.setSize(box.width, box.height);
38549     },
38550     
38551     /**
38552      * Returns the container element for this region.
38553      * @return {Roo.Element}
38554      */
38555     getEl : function(){
38556         return this.activePanel;
38557     },
38558     
38559     /**
38560      * Returns true if this region is currently visible.
38561      * @return {Boolean}
38562      */
38563     isVisible : function(){
38564         return this.activePanel ? true : false;
38565     },
38566     
38567     setActivePanel : function(panel){
38568         panel = this.getPanel(panel);
38569         if(this.activePanel && this.activePanel != panel){
38570             this.activePanel.setActiveState(false);
38571             this.activePanel.getEl().setLeftTop(-10000,-10000);
38572         }
38573         this.activePanel = panel;
38574         panel.setActiveState(true);
38575         if(this.box){
38576             panel.setSize(this.box.width, this.box.height);
38577         }
38578         this.fireEvent("panelactivated", this, panel);
38579         this.fireEvent("invalidated");
38580     },
38581     
38582     /**
38583      * Show the specified panel.
38584      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38585      * @return {Roo.ContentPanel} The shown panel or null
38586      */
38587     showPanel : function(panel){
38588         panel = this.getPanel(panel);
38589         if(panel){
38590             this.setActivePanel(panel);
38591         }
38592         return panel;
38593     },
38594     
38595     /**
38596      * Get the active panel for this region.
38597      * @return {Roo.ContentPanel} The active panel or null
38598      */
38599     getActivePanel : function(){
38600         return this.activePanel;
38601     },
38602     
38603     /**
38604      * Add the passed ContentPanel(s)
38605      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38606      * @return {Roo.ContentPanel} The panel added (if only one was added)
38607      */
38608     add : function(panel){
38609         if(arguments.length > 1){
38610             for(var i = 0, len = arguments.length; i < len; i++) {
38611                 this.add(arguments[i]);
38612             }
38613             return null;
38614         }
38615         if(this.hasPanel(panel)){
38616             this.showPanel(panel);
38617             return panel;
38618         }
38619         var el = panel.getEl();
38620         if(el.dom.parentNode != this.mgr.el.dom){
38621             this.mgr.el.dom.appendChild(el.dom);
38622         }
38623         if(panel.setRegion){
38624             panel.setRegion(this);
38625         }
38626         this.panels.add(panel);
38627         el.setStyle("position", "absolute");
38628         if(!panel.background){
38629             this.setActivePanel(panel);
38630             if(this.config.initialSize && this.panels.getCount()==1){
38631                 this.resizeTo(this.config.initialSize);
38632             }
38633         }
38634         this.fireEvent("paneladded", this, panel);
38635         return panel;
38636     },
38637     
38638     /**
38639      * Returns true if the panel is in this region.
38640      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38641      * @return {Boolean}
38642      */
38643     hasPanel : function(panel){
38644         if(typeof panel == "object"){ // must be panel obj
38645             panel = panel.getId();
38646         }
38647         return this.getPanel(panel) ? true : false;
38648     },
38649     
38650     /**
38651      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38653      * @param {Boolean} preservePanel Overrides the config preservePanel option
38654      * @return {Roo.ContentPanel} The panel that was removed
38655      */
38656     remove : function(panel, preservePanel){
38657         panel = this.getPanel(panel);
38658         if(!panel){
38659             return null;
38660         }
38661         var e = {};
38662         this.fireEvent("beforeremove", this, panel, e);
38663         if(e.cancel === true){
38664             return null;
38665         }
38666         var panelId = panel.getId();
38667         this.panels.removeKey(panelId);
38668         return panel;
38669     },
38670     
38671     /**
38672      * Returns the panel specified or null if it's not in this region.
38673      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38674      * @return {Roo.ContentPanel}
38675      */
38676     getPanel : function(id){
38677         if(typeof id == "object"){ // must be panel obj
38678             return id;
38679         }
38680         return this.panels.get(id);
38681     },
38682     
38683     /**
38684      * Returns this regions position (north/south/east/west/center).
38685      * @return {String} 
38686      */
38687     getPosition: function(){
38688         return this.position;    
38689     }
38690 });/*
38691  * Based on:
38692  * Ext JS Library 1.1.1
38693  * Copyright(c) 2006-2007, Ext JS, LLC.
38694  *
38695  * Originally Released Under LGPL - original licence link has changed is not relivant.
38696  *
38697  * Fork - LGPL
38698  * <script type="text/javascript">
38699  */
38700  
38701 /**
38702  * @class Roo.bootstrap.layout.Region
38703  * @extends Roo.bootstrap.layout.Basic
38704  * This class represents a region in a layout manager.
38705  
38706  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38707  * @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})
38708  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38709  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38710  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38711  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38712  * @cfg {String}    title           The title for the region (overrides panel titles)
38713  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38714  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38715  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38716  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38717  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38718  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38719  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38720  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38721  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38722  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38723
38724  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38725  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38726  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38727  * @cfg {Number}    width           For East/West panels
38728  * @cfg {Number}    height          For North/South panels
38729  * @cfg {Boolean}   split           To show the splitter
38730  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38731  * 
38732  * @cfg {string}   cls             Extra CSS classes to add to region
38733  * 
38734  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38735  * @cfg {string}   region  the region that it inhabits..
38736  *
38737
38738  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38739  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38740
38741  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38742  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38743  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38744  */
38745 Roo.bootstrap.layout.Region = function(config)
38746 {
38747     this.applyConfig(config);
38748
38749     var mgr = config.mgr;
38750     var pos = config.region;
38751     config.skipConfig = true;
38752     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38753     
38754     if (mgr.el) {
38755         this.onRender(mgr.el);   
38756     }
38757      
38758     this.visible = true;
38759     this.collapsed = false;
38760     this.unrendered_panels = [];
38761 };
38762
38763 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38764
38765     position: '', // set by wrapper (eg. north/south etc..)
38766     unrendered_panels : null,  // unrendered panels.
38767     
38768     tabPosition : false,
38769     
38770     mgr: false, // points to 'Border'
38771     
38772     
38773     createBody : function(){
38774         /** This region's body element 
38775         * @type Roo.Element */
38776         this.bodyEl = this.el.createChild({
38777                 tag: "div",
38778                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38779         });
38780     },
38781
38782     onRender: function(ctr, pos)
38783     {
38784         var dh = Roo.DomHelper;
38785         /** This region's container element 
38786         * @type Roo.Element */
38787         this.el = dh.append(ctr.dom, {
38788                 tag: "div",
38789                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38790             }, true);
38791         /** This region's title element 
38792         * @type Roo.Element */
38793     
38794         this.titleEl = dh.append(this.el.dom,  {
38795                 tag: "div",
38796                 unselectable: "on",
38797                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38798                 children:[
38799                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38800                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38801                 ]
38802             }, true);
38803         
38804         this.titleEl.enableDisplayMode();
38805         /** This region's title text element 
38806         * @type HTMLElement */
38807         this.titleTextEl = this.titleEl.dom.firstChild;
38808         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38809         /*
38810         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38811         this.closeBtn.enableDisplayMode();
38812         this.closeBtn.on("click", this.closeClicked, this);
38813         this.closeBtn.hide();
38814     */
38815         this.createBody(this.config);
38816         if(this.config.hideWhenEmpty){
38817             this.hide();
38818             this.on("paneladded", this.validateVisibility, this);
38819             this.on("panelremoved", this.validateVisibility, this);
38820         }
38821         if(this.autoScroll){
38822             this.bodyEl.setStyle("overflow", "auto");
38823         }else{
38824             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38825         }
38826         //if(c.titlebar !== false){
38827             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38828                 this.titleEl.hide();
38829             }else{
38830                 this.titleEl.show();
38831                 if(this.config.title){
38832                     this.titleTextEl.innerHTML = this.config.title;
38833                 }
38834             }
38835         //}
38836         if(this.config.collapsed){
38837             this.collapse(true);
38838         }
38839         if(this.config.hidden){
38840             this.hide();
38841         }
38842         
38843         if (this.unrendered_panels && this.unrendered_panels.length) {
38844             for (var i =0;i< this.unrendered_panels.length; i++) {
38845                 this.add(this.unrendered_panels[i]);
38846             }
38847             this.unrendered_panels = null;
38848             
38849         }
38850         
38851     },
38852     
38853     applyConfig : function(c)
38854     {
38855         /*
38856          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38857             var dh = Roo.DomHelper;
38858             if(c.titlebar !== false){
38859                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38860                 this.collapseBtn.on("click", this.collapse, this);
38861                 this.collapseBtn.enableDisplayMode();
38862                 /*
38863                 if(c.showPin === true || this.showPin){
38864                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38865                     this.stickBtn.enableDisplayMode();
38866                     this.stickBtn.on("click", this.expand, this);
38867                     this.stickBtn.hide();
38868                 }
38869                 
38870             }
38871             */
38872             /** This region's collapsed element
38873             * @type Roo.Element */
38874             /*
38875              *
38876             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38877                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38878             ]}, true);
38879             
38880             if(c.floatable !== false){
38881                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38882                this.collapsedEl.on("click", this.collapseClick, this);
38883             }
38884
38885             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38886                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38887                    id: "message", unselectable: "on", style:{"float":"left"}});
38888                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38889              }
38890             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38891             this.expandBtn.on("click", this.expand, this);
38892             
38893         }
38894         
38895         if(this.collapseBtn){
38896             this.collapseBtn.setVisible(c.collapsible == true);
38897         }
38898         
38899         this.cmargins = c.cmargins || this.cmargins ||
38900                          (this.position == "west" || this.position == "east" ?
38901                              {top: 0, left: 2, right:2, bottom: 0} :
38902                              {top: 2, left: 0, right:0, bottom: 2});
38903         */
38904         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38905         
38906         
38907         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38908         
38909         this.autoScroll = c.autoScroll || false;
38910         
38911         
38912        
38913         
38914         this.duration = c.duration || .30;
38915         this.slideDuration = c.slideDuration || .45;
38916         this.config = c;
38917        
38918     },
38919     /**
38920      * Returns true if this region is currently visible.
38921      * @return {Boolean}
38922      */
38923     isVisible : function(){
38924         return this.visible;
38925     },
38926
38927     /**
38928      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38929      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38930      */
38931     //setCollapsedTitle : function(title){
38932     //    title = title || "&#160;";
38933      //   if(this.collapsedTitleTextEl){
38934       //      this.collapsedTitleTextEl.innerHTML = title;
38935        // }
38936     //},
38937
38938     getBox : function(){
38939         var b;
38940       //  if(!this.collapsed){
38941             b = this.el.getBox(false, true);
38942        // }else{
38943           //  b = this.collapsedEl.getBox(false, true);
38944         //}
38945         return b;
38946     },
38947
38948     getMargins : function(){
38949         return this.margins;
38950         //return this.collapsed ? this.cmargins : this.margins;
38951     },
38952 /*
38953     highlight : function(){
38954         this.el.addClass("x-layout-panel-dragover");
38955     },
38956
38957     unhighlight : function(){
38958         this.el.removeClass("x-layout-panel-dragover");
38959     },
38960 */
38961     updateBox : function(box)
38962     {
38963         if (!this.bodyEl) {
38964             return; // not rendered yet..
38965         }
38966         
38967         this.box = box;
38968         if(!this.collapsed){
38969             this.el.dom.style.left = box.x + "px";
38970             this.el.dom.style.top = box.y + "px";
38971             this.updateBody(box.width, box.height);
38972         }else{
38973             this.collapsedEl.dom.style.left = box.x + "px";
38974             this.collapsedEl.dom.style.top = box.y + "px";
38975             this.collapsedEl.setSize(box.width, box.height);
38976         }
38977         if(this.tabs){
38978             this.tabs.autoSizeTabs();
38979         }
38980     },
38981
38982     updateBody : function(w, h)
38983     {
38984         if(w !== null){
38985             this.el.setWidth(w);
38986             w -= this.el.getBorderWidth("rl");
38987             if(this.config.adjustments){
38988                 w += this.config.adjustments[0];
38989             }
38990         }
38991         if(h !== null && h > 0){
38992             this.el.setHeight(h);
38993             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38994             h -= this.el.getBorderWidth("tb");
38995             if(this.config.adjustments){
38996                 h += this.config.adjustments[1];
38997             }
38998             this.bodyEl.setHeight(h);
38999             if(this.tabs){
39000                 h = this.tabs.syncHeight(h);
39001             }
39002         }
39003         if(this.panelSize){
39004             w = w !== null ? w : this.panelSize.width;
39005             h = h !== null ? h : this.panelSize.height;
39006         }
39007         if(this.activePanel){
39008             var el = this.activePanel.getEl();
39009             w = w !== null ? w : el.getWidth();
39010             h = h !== null ? h : el.getHeight();
39011             this.panelSize = {width: w, height: h};
39012             this.activePanel.setSize(w, h);
39013         }
39014         if(Roo.isIE && this.tabs){
39015             this.tabs.el.repaint();
39016         }
39017     },
39018
39019     /**
39020      * Returns the container element for this region.
39021      * @return {Roo.Element}
39022      */
39023     getEl : function(){
39024         return this.el;
39025     },
39026
39027     /**
39028      * Hides this region.
39029      */
39030     hide : function(){
39031         //if(!this.collapsed){
39032             this.el.dom.style.left = "-2000px";
39033             this.el.hide();
39034         //}else{
39035          //   this.collapsedEl.dom.style.left = "-2000px";
39036          //   this.collapsedEl.hide();
39037        // }
39038         this.visible = false;
39039         this.fireEvent("visibilitychange", this, false);
39040     },
39041
39042     /**
39043      * Shows this region if it was previously hidden.
39044      */
39045     show : function(){
39046         //if(!this.collapsed){
39047             this.el.show();
39048         //}else{
39049         //    this.collapsedEl.show();
39050        // }
39051         this.visible = true;
39052         this.fireEvent("visibilitychange", this, true);
39053     },
39054 /*
39055     closeClicked : function(){
39056         if(this.activePanel){
39057             this.remove(this.activePanel);
39058         }
39059     },
39060
39061     collapseClick : function(e){
39062         if(this.isSlid){
39063            e.stopPropagation();
39064            this.slideIn();
39065         }else{
39066            e.stopPropagation();
39067            this.slideOut();
39068         }
39069     },
39070 */
39071     /**
39072      * Collapses this region.
39073      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39074      */
39075     /*
39076     collapse : function(skipAnim, skipCheck = false){
39077         if(this.collapsed) {
39078             return;
39079         }
39080         
39081         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39082             
39083             this.collapsed = true;
39084             if(this.split){
39085                 this.split.el.hide();
39086             }
39087             if(this.config.animate && skipAnim !== true){
39088                 this.fireEvent("invalidated", this);
39089                 this.animateCollapse();
39090             }else{
39091                 this.el.setLocation(-20000,-20000);
39092                 this.el.hide();
39093                 this.collapsedEl.show();
39094                 this.fireEvent("collapsed", this);
39095                 this.fireEvent("invalidated", this);
39096             }
39097         }
39098         
39099     },
39100 */
39101     animateCollapse : function(){
39102         // overridden
39103     },
39104
39105     /**
39106      * Expands this region if it was previously collapsed.
39107      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39108      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39109      */
39110     /*
39111     expand : function(e, skipAnim){
39112         if(e) {
39113             e.stopPropagation();
39114         }
39115         if(!this.collapsed || this.el.hasActiveFx()) {
39116             return;
39117         }
39118         if(this.isSlid){
39119             this.afterSlideIn();
39120             skipAnim = true;
39121         }
39122         this.collapsed = false;
39123         if(this.config.animate && skipAnim !== true){
39124             this.animateExpand();
39125         }else{
39126             this.el.show();
39127             if(this.split){
39128                 this.split.el.show();
39129             }
39130             this.collapsedEl.setLocation(-2000,-2000);
39131             this.collapsedEl.hide();
39132             this.fireEvent("invalidated", this);
39133             this.fireEvent("expanded", this);
39134         }
39135     },
39136 */
39137     animateExpand : function(){
39138         // overridden
39139     },
39140
39141     initTabs : function()
39142     {
39143         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39144         
39145         var ts = new Roo.bootstrap.panel.Tabs({
39146             el: this.bodyEl.dom,
39147             region : this,
39148             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39149             disableTooltips: this.config.disableTabTips,
39150             toolbar : this.config.toolbar
39151         });
39152         
39153         if(this.config.hideTabs){
39154             ts.stripWrap.setDisplayed(false);
39155         }
39156         this.tabs = ts;
39157         ts.resizeTabs = this.config.resizeTabs === true;
39158         ts.minTabWidth = this.config.minTabWidth || 40;
39159         ts.maxTabWidth = this.config.maxTabWidth || 250;
39160         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39161         ts.monitorResize = false;
39162         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39163         ts.bodyEl.addClass('roo-layout-tabs-body');
39164         this.panels.each(this.initPanelAsTab, this);
39165     },
39166
39167     initPanelAsTab : function(panel){
39168         var ti = this.tabs.addTab(
39169             panel.getEl().id,
39170             panel.getTitle(),
39171             null,
39172             this.config.closeOnTab && panel.isClosable(),
39173             panel.tpl
39174         );
39175         if(panel.tabTip !== undefined){
39176             ti.setTooltip(panel.tabTip);
39177         }
39178         ti.on("activate", function(){
39179               this.setActivePanel(panel);
39180         }, this);
39181         
39182         if(this.config.closeOnTab){
39183             ti.on("beforeclose", function(t, e){
39184                 e.cancel = true;
39185                 this.remove(panel);
39186             }, this);
39187         }
39188         
39189         panel.tabItem = ti;
39190         
39191         return ti;
39192     },
39193
39194     updatePanelTitle : function(panel, title)
39195     {
39196         if(this.activePanel == panel){
39197             this.updateTitle(title);
39198         }
39199         if(this.tabs){
39200             var ti = this.tabs.getTab(panel.getEl().id);
39201             ti.setText(title);
39202             if(panel.tabTip !== undefined){
39203                 ti.setTooltip(panel.tabTip);
39204             }
39205         }
39206     },
39207
39208     updateTitle : function(title){
39209         if(this.titleTextEl && !this.config.title){
39210             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39211         }
39212     },
39213
39214     setActivePanel : function(panel)
39215     {
39216         panel = this.getPanel(panel);
39217         if(this.activePanel && this.activePanel != panel){
39218             if(this.activePanel.setActiveState(false) === false){
39219                 return;
39220             }
39221         }
39222         this.activePanel = panel;
39223         panel.setActiveState(true);
39224         if(this.panelSize){
39225             panel.setSize(this.panelSize.width, this.panelSize.height);
39226         }
39227         if(this.closeBtn){
39228             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39229         }
39230         this.updateTitle(panel.getTitle());
39231         if(this.tabs){
39232             this.fireEvent("invalidated", this);
39233         }
39234         this.fireEvent("panelactivated", this, panel);
39235     },
39236
39237     /**
39238      * Shows the specified panel.
39239      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39240      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39241      */
39242     showPanel : function(panel)
39243     {
39244         panel = this.getPanel(panel);
39245         if(panel){
39246             if(this.tabs){
39247                 var tab = this.tabs.getTab(panel.getEl().id);
39248                 if(tab.isHidden()){
39249                     this.tabs.unhideTab(tab.id);
39250                 }
39251                 tab.activate();
39252             }else{
39253                 this.setActivePanel(panel);
39254             }
39255         }
39256         return panel;
39257     },
39258
39259     /**
39260      * Get the active panel for this region.
39261      * @return {Roo.ContentPanel} The active panel or null
39262      */
39263     getActivePanel : function(){
39264         return this.activePanel;
39265     },
39266
39267     validateVisibility : function(){
39268         if(this.panels.getCount() < 1){
39269             this.updateTitle("&#160;");
39270             this.closeBtn.hide();
39271             this.hide();
39272         }else{
39273             if(!this.isVisible()){
39274                 this.show();
39275             }
39276         }
39277     },
39278
39279     /**
39280      * Adds the passed ContentPanel(s) to this region.
39281      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39282      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39283      */
39284     add : function(panel)
39285     {
39286         if(arguments.length > 1){
39287             for(var i = 0, len = arguments.length; i < len; i++) {
39288                 this.add(arguments[i]);
39289             }
39290             return null;
39291         }
39292         
39293         // if we have not been rendered yet, then we can not really do much of this..
39294         if (!this.bodyEl) {
39295             this.unrendered_panels.push(panel);
39296             return panel;
39297         }
39298         
39299         
39300         
39301         
39302         if(this.hasPanel(panel)){
39303             this.showPanel(panel);
39304             return panel;
39305         }
39306         panel.setRegion(this);
39307         this.panels.add(panel);
39308        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39309             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39310             // and hide them... ???
39311             this.bodyEl.dom.appendChild(panel.getEl().dom);
39312             if(panel.background !== true){
39313                 this.setActivePanel(panel);
39314             }
39315             this.fireEvent("paneladded", this, panel);
39316             return panel;
39317         }
39318         */
39319         if(!this.tabs){
39320             this.initTabs();
39321         }else{
39322             this.initPanelAsTab(panel);
39323         }
39324         
39325         
39326         if(panel.background !== true){
39327             this.tabs.activate(panel.getEl().id);
39328         }
39329         this.fireEvent("paneladded", this, panel);
39330         return panel;
39331     },
39332
39333     /**
39334      * Hides the tab for the specified panel.
39335      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39336      */
39337     hidePanel : function(panel){
39338         if(this.tabs && (panel = this.getPanel(panel))){
39339             this.tabs.hideTab(panel.getEl().id);
39340         }
39341     },
39342
39343     /**
39344      * Unhides the tab for a previously hidden panel.
39345      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39346      */
39347     unhidePanel : function(panel){
39348         if(this.tabs && (panel = this.getPanel(panel))){
39349             this.tabs.unhideTab(panel.getEl().id);
39350         }
39351     },
39352
39353     clearPanels : function(){
39354         while(this.panels.getCount() > 0){
39355              this.remove(this.panels.first());
39356         }
39357     },
39358
39359     /**
39360      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39361      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39362      * @param {Boolean} preservePanel Overrides the config preservePanel option
39363      * @return {Roo.ContentPanel} The panel that was removed
39364      */
39365     remove : function(panel, preservePanel)
39366     {
39367         panel = this.getPanel(panel);
39368         if(!panel){
39369             return null;
39370         }
39371         var e = {};
39372         this.fireEvent("beforeremove", this, panel, e);
39373         if(e.cancel === true){
39374             return null;
39375         }
39376         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39377         var panelId = panel.getId();
39378         this.panels.removeKey(panelId);
39379         if(preservePanel){
39380             document.body.appendChild(panel.getEl().dom);
39381         }
39382         if(this.tabs){
39383             this.tabs.removeTab(panel.getEl().id);
39384         }else if (!preservePanel){
39385             this.bodyEl.dom.removeChild(panel.getEl().dom);
39386         }
39387         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39388             var p = this.panels.first();
39389             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39390             tempEl.appendChild(p.getEl().dom);
39391             this.bodyEl.update("");
39392             this.bodyEl.dom.appendChild(p.getEl().dom);
39393             tempEl = null;
39394             this.updateTitle(p.getTitle());
39395             this.tabs = null;
39396             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39397             this.setActivePanel(p);
39398         }
39399         panel.setRegion(null);
39400         if(this.activePanel == panel){
39401             this.activePanel = null;
39402         }
39403         if(this.config.autoDestroy !== false && preservePanel !== true){
39404             try{panel.destroy();}catch(e){}
39405         }
39406         this.fireEvent("panelremoved", this, panel);
39407         return panel;
39408     },
39409
39410     /**
39411      * Returns the TabPanel component used by this region
39412      * @return {Roo.TabPanel}
39413      */
39414     getTabs : function(){
39415         return this.tabs;
39416     },
39417
39418     createTool : function(parentEl, className){
39419         var btn = Roo.DomHelper.append(parentEl, {
39420             tag: "div",
39421             cls: "x-layout-tools-button",
39422             children: [ {
39423                 tag: "div",
39424                 cls: "roo-layout-tools-button-inner " + className,
39425                 html: "&#160;"
39426             }]
39427         }, true);
39428         btn.addClassOnOver("roo-layout-tools-button-over");
39429         return btn;
39430     }
39431 });/*
39432  * Based on:
39433  * Ext JS Library 1.1.1
39434  * Copyright(c) 2006-2007, Ext JS, LLC.
39435  *
39436  * Originally Released Under LGPL - original licence link has changed is not relivant.
39437  *
39438  * Fork - LGPL
39439  * <script type="text/javascript">
39440  */
39441  
39442
39443
39444 /**
39445  * @class Roo.SplitLayoutRegion
39446  * @extends Roo.LayoutRegion
39447  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39448  */
39449 Roo.bootstrap.layout.Split = function(config){
39450     this.cursor = config.cursor;
39451     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39452 };
39453
39454 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39455 {
39456     splitTip : "Drag to resize.",
39457     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39458     useSplitTips : false,
39459
39460     applyConfig : function(config){
39461         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39462     },
39463     
39464     onRender : function(ctr,pos) {
39465         
39466         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39467         if(!this.config.split){
39468             return;
39469         }
39470         if(!this.split){
39471             
39472             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39473                             tag: "div",
39474                             id: this.el.id + "-split",
39475                             cls: "roo-layout-split roo-layout-split-"+this.position,
39476                             html: "&#160;"
39477             });
39478             /** The SplitBar for this region 
39479             * @type Roo.SplitBar */
39480             // does not exist yet...
39481             Roo.log([this.position, this.orientation]);
39482             
39483             this.split = new Roo.bootstrap.SplitBar({
39484                 dragElement : splitEl,
39485                 resizingElement: this.el,
39486                 orientation : this.orientation
39487             });
39488             
39489             this.split.on("moved", this.onSplitMove, this);
39490             this.split.useShim = this.config.useShim === true;
39491             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39492             if(this.useSplitTips){
39493                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39494             }
39495             //if(config.collapsible){
39496             //    this.split.el.on("dblclick", this.collapse,  this);
39497             //}
39498         }
39499         if(typeof this.config.minSize != "undefined"){
39500             this.split.minSize = this.config.minSize;
39501         }
39502         if(typeof this.config.maxSize != "undefined"){
39503             this.split.maxSize = this.config.maxSize;
39504         }
39505         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39506             this.hideSplitter();
39507         }
39508         
39509     },
39510
39511     getHMaxSize : function(){
39512          var cmax = this.config.maxSize || 10000;
39513          var center = this.mgr.getRegion("center");
39514          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39515     },
39516
39517     getVMaxSize : function(){
39518          var cmax = this.config.maxSize || 10000;
39519          var center = this.mgr.getRegion("center");
39520          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39521     },
39522
39523     onSplitMove : function(split, newSize){
39524         this.fireEvent("resized", this, newSize);
39525     },
39526     
39527     /** 
39528      * Returns the {@link Roo.SplitBar} for this region.
39529      * @return {Roo.SplitBar}
39530      */
39531     getSplitBar : function(){
39532         return this.split;
39533     },
39534     
39535     hide : function(){
39536         this.hideSplitter();
39537         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39538     },
39539
39540     hideSplitter : function(){
39541         if(this.split){
39542             this.split.el.setLocation(-2000,-2000);
39543             this.split.el.hide();
39544         }
39545     },
39546
39547     show : function(){
39548         if(this.split){
39549             this.split.el.show();
39550         }
39551         Roo.bootstrap.layout.Split.superclass.show.call(this);
39552     },
39553     
39554     beforeSlide: function(){
39555         if(Roo.isGecko){// firefox overflow auto bug workaround
39556             this.bodyEl.clip();
39557             if(this.tabs) {
39558                 this.tabs.bodyEl.clip();
39559             }
39560             if(this.activePanel){
39561                 this.activePanel.getEl().clip();
39562                 
39563                 if(this.activePanel.beforeSlide){
39564                     this.activePanel.beforeSlide();
39565                 }
39566             }
39567         }
39568     },
39569     
39570     afterSlide : function(){
39571         if(Roo.isGecko){// firefox overflow auto bug workaround
39572             this.bodyEl.unclip();
39573             if(this.tabs) {
39574                 this.tabs.bodyEl.unclip();
39575             }
39576             if(this.activePanel){
39577                 this.activePanel.getEl().unclip();
39578                 if(this.activePanel.afterSlide){
39579                     this.activePanel.afterSlide();
39580                 }
39581             }
39582         }
39583     },
39584
39585     initAutoHide : function(){
39586         if(this.autoHide !== false){
39587             if(!this.autoHideHd){
39588                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39589                 this.autoHideHd = {
39590                     "mouseout": function(e){
39591                         if(!e.within(this.el, true)){
39592                             st.delay(500);
39593                         }
39594                     },
39595                     "mouseover" : function(e){
39596                         st.cancel();
39597                     },
39598                     scope : this
39599                 };
39600             }
39601             this.el.on(this.autoHideHd);
39602         }
39603     },
39604
39605     clearAutoHide : function(){
39606         if(this.autoHide !== false){
39607             this.el.un("mouseout", this.autoHideHd.mouseout);
39608             this.el.un("mouseover", this.autoHideHd.mouseover);
39609         }
39610     },
39611
39612     clearMonitor : function(){
39613         Roo.get(document).un("click", this.slideInIf, this);
39614     },
39615
39616     // these names are backwards but not changed for compat
39617     slideOut : function(){
39618         if(this.isSlid || this.el.hasActiveFx()){
39619             return;
39620         }
39621         this.isSlid = true;
39622         if(this.collapseBtn){
39623             this.collapseBtn.hide();
39624         }
39625         this.closeBtnState = this.closeBtn.getStyle('display');
39626         this.closeBtn.hide();
39627         if(this.stickBtn){
39628             this.stickBtn.show();
39629         }
39630         this.el.show();
39631         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39632         this.beforeSlide();
39633         this.el.setStyle("z-index", 10001);
39634         this.el.slideIn(this.getSlideAnchor(), {
39635             callback: function(){
39636                 this.afterSlide();
39637                 this.initAutoHide();
39638                 Roo.get(document).on("click", this.slideInIf, this);
39639                 this.fireEvent("slideshow", this);
39640             },
39641             scope: this,
39642             block: true
39643         });
39644     },
39645
39646     afterSlideIn : function(){
39647         this.clearAutoHide();
39648         this.isSlid = false;
39649         this.clearMonitor();
39650         this.el.setStyle("z-index", "");
39651         if(this.collapseBtn){
39652             this.collapseBtn.show();
39653         }
39654         this.closeBtn.setStyle('display', this.closeBtnState);
39655         if(this.stickBtn){
39656             this.stickBtn.hide();
39657         }
39658         this.fireEvent("slidehide", this);
39659     },
39660
39661     slideIn : function(cb){
39662         if(!this.isSlid || this.el.hasActiveFx()){
39663             Roo.callback(cb);
39664             return;
39665         }
39666         this.isSlid = false;
39667         this.beforeSlide();
39668         this.el.slideOut(this.getSlideAnchor(), {
39669             callback: function(){
39670                 this.el.setLeftTop(-10000, -10000);
39671                 this.afterSlide();
39672                 this.afterSlideIn();
39673                 Roo.callback(cb);
39674             },
39675             scope: this,
39676             block: true
39677         });
39678     },
39679     
39680     slideInIf : function(e){
39681         if(!e.within(this.el)){
39682             this.slideIn();
39683         }
39684     },
39685
39686     animateCollapse : function(){
39687         this.beforeSlide();
39688         this.el.setStyle("z-index", 20000);
39689         var anchor = this.getSlideAnchor();
39690         this.el.slideOut(anchor, {
39691             callback : function(){
39692                 this.el.setStyle("z-index", "");
39693                 this.collapsedEl.slideIn(anchor, {duration:.3});
39694                 this.afterSlide();
39695                 this.el.setLocation(-10000,-10000);
39696                 this.el.hide();
39697                 this.fireEvent("collapsed", this);
39698             },
39699             scope: this,
39700             block: true
39701         });
39702     },
39703
39704     animateExpand : function(){
39705         this.beforeSlide();
39706         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39707         this.el.setStyle("z-index", 20000);
39708         this.collapsedEl.hide({
39709             duration:.1
39710         });
39711         this.el.slideIn(this.getSlideAnchor(), {
39712             callback : function(){
39713                 this.el.setStyle("z-index", "");
39714                 this.afterSlide();
39715                 if(this.split){
39716                     this.split.el.show();
39717                 }
39718                 this.fireEvent("invalidated", this);
39719                 this.fireEvent("expanded", this);
39720             },
39721             scope: this,
39722             block: true
39723         });
39724     },
39725
39726     anchors : {
39727         "west" : "left",
39728         "east" : "right",
39729         "north" : "top",
39730         "south" : "bottom"
39731     },
39732
39733     sanchors : {
39734         "west" : "l",
39735         "east" : "r",
39736         "north" : "t",
39737         "south" : "b"
39738     },
39739
39740     canchors : {
39741         "west" : "tl-tr",
39742         "east" : "tr-tl",
39743         "north" : "tl-bl",
39744         "south" : "bl-tl"
39745     },
39746
39747     getAnchor : function(){
39748         return this.anchors[this.position];
39749     },
39750
39751     getCollapseAnchor : function(){
39752         return this.canchors[this.position];
39753     },
39754
39755     getSlideAnchor : function(){
39756         return this.sanchors[this.position];
39757     },
39758
39759     getAlignAdj : function(){
39760         var cm = this.cmargins;
39761         switch(this.position){
39762             case "west":
39763                 return [0, 0];
39764             break;
39765             case "east":
39766                 return [0, 0];
39767             break;
39768             case "north":
39769                 return [0, 0];
39770             break;
39771             case "south":
39772                 return [0, 0];
39773             break;
39774         }
39775     },
39776
39777     getExpandAdj : function(){
39778         var c = this.collapsedEl, cm = this.cmargins;
39779         switch(this.position){
39780             case "west":
39781                 return [-(cm.right+c.getWidth()+cm.left), 0];
39782             break;
39783             case "east":
39784                 return [cm.right+c.getWidth()+cm.left, 0];
39785             break;
39786             case "north":
39787                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39788             break;
39789             case "south":
39790                 return [0, cm.top+cm.bottom+c.getHeight()];
39791             break;
39792         }
39793     }
39794 });/*
39795  * Based on:
39796  * Ext JS Library 1.1.1
39797  * Copyright(c) 2006-2007, Ext JS, LLC.
39798  *
39799  * Originally Released Under LGPL - original licence link has changed is not relivant.
39800  *
39801  * Fork - LGPL
39802  * <script type="text/javascript">
39803  */
39804 /*
39805  * These classes are private internal classes
39806  */
39807 Roo.bootstrap.layout.Center = function(config){
39808     config.region = "center";
39809     Roo.bootstrap.layout.Region.call(this, config);
39810     this.visible = true;
39811     this.minWidth = config.minWidth || 20;
39812     this.minHeight = config.minHeight || 20;
39813 };
39814
39815 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39816     hide : function(){
39817         // center panel can't be hidden
39818     },
39819     
39820     show : function(){
39821         // center panel can't be hidden
39822     },
39823     
39824     getMinWidth: function(){
39825         return this.minWidth;
39826     },
39827     
39828     getMinHeight: function(){
39829         return this.minHeight;
39830     }
39831 });
39832
39833
39834
39835
39836  
39837
39838
39839
39840
39841
39842
39843 Roo.bootstrap.layout.North = function(config)
39844 {
39845     config.region = 'north';
39846     config.cursor = 'n-resize';
39847     
39848     Roo.bootstrap.layout.Split.call(this, config);
39849     
39850     
39851     if(this.split){
39852         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39853         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39854         this.split.el.addClass("roo-layout-split-v");
39855     }
39856     //var size = config.initialSize || config.height;
39857     //if(this.el && typeof size != "undefined"){
39858     //    this.el.setHeight(size);
39859     //}
39860 };
39861 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39862 {
39863     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39864      
39865      
39866     onRender : function(ctr, pos)
39867     {
39868         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39869         var size = this.config.initialSize || this.config.height;
39870         if(this.el && typeof size != "undefined"){
39871             this.el.setHeight(size);
39872         }
39873     
39874     },
39875     
39876     getBox : function(){
39877         if(this.collapsed){
39878             return this.collapsedEl.getBox();
39879         }
39880         var box = this.el.getBox();
39881         if(this.split){
39882             box.height += this.split.el.getHeight();
39883         }
39884         return box;
39885     },
39886     
39887     updateBox : function(box){
39888         if(this.split && !this.collapsed){
39889             box.height -= this.split.el.getHeight();
39890             this.split.el.setLeft(box.x);
39891             this.split.el.setTop(box.y+box.height);
39892             this.split.el.setWidth(box.width);
39893         }
39894         if(this.collapsed){
39895             this.updateBody(box.width, null);
39896         }
39897         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39898     }
39899 });
39900
39901
39902
39903
39904
39905 Roo.bootstrap.layout.South = function(config){
39906     config.region = 'south';
39907     config.cursor = 's-resize';
39908     Roo.bootstrap.layout.Split.call(this, config);
39909     if(this.split){
39910         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39911         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39912         this.split.el.addClass("roo-layout-split-v");
39913     }
39914     
39915 };
39916
39917 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39918     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39919     
39920     onRender : function(ctr, pos)
39921     {
39922         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39923         var size = this.config.initialSize || this.config.height;
39924         if(this.el && typeof size != "undefined"){
39925             this.el.setHeight(size);
39926         }
39927     
39928     },
39929     
39930     getBox : function(){
39931         if(this.collapsed){
39932             return this.collapsedEl.getBox();
39933         }
39934         var box = this.el.getBox();
39935         if(this.split){
39936             var sh = this.split.el.getHeight();
39937             box.height += sh;
39938             box.y -= sh;
39939         }
39940         return box;
39941     },
39942     
39943     updateBox : function(box){
39944         if(this.split && !this.collapsed){
39945             var sh = this.split.el.getHeight();
39946             box.height -= sh;
39947             box.y += sh;
39948             this.split.el.setLeft(box.x);
39949             this.split.el.setTop(box.y-sh);
39950             this.split.el.setWidth(box.width);
39951         }
39952         if(this.collapsed){
39953             this.updateBody(box.width, null);
39954         }
39955         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39956     }
39957 });
39958
39959 Roo.bootstrap.layout.East = function(config){
39960     config.region = "east";
39961     config.cursor = "e-resize";
39962     Roo.bootstrap.layout.Split.call(this, config);
39963     if(this.split){
39964         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39965         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39966         this.split.el.addClass("roo-layout-split-h");
39967     }
39968     
39969 };
39970 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39971     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39972     
39973     onRender : function(ctr, pos)
39974     {
39975         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39976         var size = this.config.initialSize || this.config.width;
39977         if(this.el && typeof size != "undefined"){
39978             this.el.setWidth(size);
39979         }
39980     
39981     },
39982     
39983     getBox : function(){
39984         if(this.collapsed){
39985             return this.collapsedEl.getBox();
39986         }
39987         var box = this.el.getBox();
39988         if(this.split){
39989             var sw = this.split.el.getWidth();
39990             box.width += sw;
39991             box.x -= sw;
39992         }
39993         return box;
39994     },
39995
39996     updateBox : function(box){
39997         if(this.split && !this.collapsed){
39998             var sw = this.split.el.getWidth();
39999             box.width -= sw;
40000             this.split.el.setLeft(box.x);
40001             this.split.el.setTop(box.y);
40002             this.split.el.setHeight(box.height);
40003             box.x += sw;
40004         }
40005         if(this.collapsed){
40006             this.updateBody(null, box.height);
40007         }
40008         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40009     }
40010 });
40011
40012 Roo.bootstrap.layout.West = function(config){
40013     config.region = "west";
40014     config.cursor = "w-resize";
40015     
40016     Roo.bootstrap.layout.Split.call(this, config);
40017     if(this.split){
40018         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40019         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40020         this.split.el.addClass("roo-layout-split-h");
40021     }
40022     
40023 };
40024 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40025     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40026     
40027     onRender: function(ctr, pos)
40028     {
40029         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40030         var size = this.config.initialSize || this.config.width;
40031         if(typeof size != "undefined"){
40032             this.el.setWidth(size);
40033         }
40034     },
40035     
40036     getBox : function(){
40037         if(this.collapsed){
40038             return this.collapsedEl.getBox();
40039         }
40040         var box = this.el.getBox();
40041         if (box.width == 0) {
40042             box.width = this.config.width; // kludge?
40043         }
40044         if(this.split){
40045             box.width += this.split.el.getWidth();
40046         }
40047         return box;
40048     },
40049     
40050     updateBox : function(box){
40051         if(this.split && !this.collapsed){
40052             var sw = this.split.el.getWidth();
40053             box.width -= sw;
40054             this.split.el.setLeft(box.x+box.width);
40055             this.split.el.setTop(box.y);
40056             this.split.el.setHeight(box.height);
40057         }
40058         if(this.collapsed){
40059             this.updateBody(null, box.height);
40060         }
40061         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40062     }
40063 });Roo.namespace("Roo.bootstrap.panel");/*
40064  * Based on:
40065  * Ext JS Library 1.1.1
40066  * Copyright(c) 2006-2007, Ext JS, LLC.
40067  *
40068  * Originally Released Under LGPL - original licence link has changed is not relivant.
40069  *
40070  * Fork - LGPL
40071  * <script type="text/javascript">
40072  */
40073 /**
40074  * @class Roo.ContentPanel
40075  * @extends Roo.util.Observable
40076  * A basic ContentPanel element.
40077  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40078  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40079  * @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
40080  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40081  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40082  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40083  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40084  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40085  * @cfg {String} title          The title for this panel
40086  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40087  * @cfg {String} url            Calls {@link #setUrl} with this value
40088  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40089  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40090  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40091  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40092  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40093  * @cfg {Boolean} badges render the badges
40094  * @cfg {String} cls  extra classes to use  
40095  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40096
40097  * @constructor
40098  * Create a new ContentPanel.
40099  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40100  * @param {String/Object} config A string to set only the title or a config object
40101  * @param {String} content (optional) Set the HTML content for this panel
40102  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40103  */
40104 Roo.bootstrap.panel.Content = function( config){
40105     
40106     this.tpl = config.tpl || false;
40107     
40108     var el = config.el;
40109     var content = config.content;
40110
40111     if(config.autoCreate){ // xtype is available if this is called from factory
40112         el = Roo.id();
40113     }
40114     this.el = Roo.get(el);
40115     if(!this.el && config && config.autoCreate){
40116         if(typeof config.autoCreate == "object"){
40117             if(!config.autoCreate.id){
40118                 config.autoCreate.id = config.id||el;
40119             }
40120             this.el = Roo.DomHelper.append(document.body,
40121                         config.autoCreate, true);
40122         }else{
40123             var elcfg =  {
40124                 tag: "div",
40125                 cls: (config.cls || '') +
40126                     (config.background ? ' bg-' + config.background : '') +
40127                     " roo-layout-inactive-content",
40128                 id: config.id||el
40129             };
40130             if (config.iframe) {
40131                 elcfg.cn = [
40132                     {
40133                         tag : 'iframe',
40134                         style : 'border: 0px',
40135                         src : 'about:blank'
40136                     }
40137                 ];
40138             }
40139               
40140             if (config.html) {
40141                 elcfg.html = config.html;
40142                 
40143             }
40144                         
40145             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40146             if (config.iframe) {
40147                 this.iframeEl = this.el.select('iframe',true).first();
40148             }
40149             
40150         }
40151     } 
40152     this.closable = false;
40153     this.loaded = false;
40154     this.active = false;
40155    
40156       
40157     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40158         
40159         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40160         
40161         this.wrapEl = this.el; //this.el.wrap();
40162         var ti = [];
40163         if (config.toolbar.items) {
40164             ti = config.toolbar.items ;
40165             delete config.toolbar.items ;
40166         }
40167         
40168         var nitems = [];
40169         this.toolbar.render(this.wrapEl, 'before');
40170         for(var i =0;i < ti.length;i++) {
40171           //  Roo.log(['add child', items[i]]);
40172             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40173         }
40174         this.toolbar.items = nitems;
40175         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40176         delete config.toolbar;
40177         
40178     }
40179     /*
40180     // xtype created footer. - not sure if will work as we normally have to render first..
40181     if (this.footer && !this.footer.el && this.footer.xtype) {
40182         if (!this.wrapEl) {
40183             this.wrapEl = this.el.wrap();
40184         }
40185     
40186         this.footer.container = this.wrapEl.createChild();
40187          
40188         this.footer = Roo.factory(this.footer, Roo);
40189         
40190     }
40191     */
40192     
40193      if(typeof config == "string"){
40194         this.title = config;
40195     }else{
40196         Roo.apply(this, config);
40197     }
40198     
40199     if(this.resizeEl){
40200         this.resizeEl = Roo.get(this.resizeEl, true);
40201     }else{
40202         this.resizeEl = this.el;
40203     }
40204     // handle view.xtype
40205     
40206  
40207     
40208     
40209     this.addEvents({
40210         /**
40211          * @event activate
40212          * Fires when this panel is activated. 
40213          * @param {Roo.ContentPanel} this
40214          */
40215         "activate" : true,
40216         /**
40217          * @event deactivate
40218          * Fires when this panel is activated. 
40219          * @param {Roo.ContentPanel} this
40220          */
40221         "deactivate" : true,
40222
40223         /**
40224          * @event resize
40225          * Fires when this panel is resized if fitToFrame is true.
40226          * @param {Roo.ContentPanel} this
40227          * @param {Number} width The width after any component adjustments
40228          * @param {Number} height The height after any component adjustments
40229          */
40230         "resize" : true,
40231         
40232          /**
40233          * @event render
40234          * Fires when this tab is created
40235          * @param {Roo.ContentPanel} this
40236          */
40237         "render" : true,
40238         
40239           /**
40240          * @event scroll
40241          * Fires when this content is scrolled
40242          * @param {Roo.ContentPanel} this
40243          * @param {Event} scrollEvent
40244          */
40245         "scroll" : true
40246         
40247         
40248         
40249     });
40250     
40251
40252     
40253     
40254     if(this.autoScroll && !this.iframe){
40255         this.resizeEl.setStyle("overflow", "auto");
40256         this.resizeEl.on('scroll', this.onScroll, this);
40257     } else {
40258         // fix randome scrolling
40259         //this.el.on('scroll', function() {
40260         //    Roo.log('fix random scolling');
40261         //    this.scrollTo('top',0); 
40262         //});
40263     }
40264     content = content || this.content;
40265     if(content){
40266         this.setContent(content);
40267     }
40268     if(config && config.url){
40269         this.setUrl(this.url, this.params, this.loadOnce);
40270     }
40271     
40272     
40273     
40274     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40275     
40276     if (this.view && typeof(this.view.xtype) != 'undefined') {
40277         this.view.el = this.el.appendChild(document.createElement("div"));
40278         this.view = Roo.factory(this.view); 
40279         this.view.render  &&  this.view.render(false, '');  
40280     }
40281     
40282     
40283     this.fireEvent('render', this);
40284 };
40285
40286 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40287     
40288     cls : '',
40289     background : '',
40290     
40291     tabTip : '',
40292     
40293     iframe : false,
40294     iframeEl : false,
40295     
40296     /* Resize Element - use this to work out scroll etc. */
40297     resizeEl : false,
40298     
40299     setRegion : function(region){
40300         this.region = region;
40301         this.setActiveClass(region && !this.background);
40302     },
40303     
40304     
40305     setActiveClass: function(state)
40306     {
40307         if(state){
40308            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40309            this.el.setStyle('position','relative');
40310         }else{
40311            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40312            this.el.setStyle('position', 'absolute');
40313         } 
40314     },
40315     
40316     /**
40317      * Returns the toolbar for this Panel if one was configured. 
40318      * @return {Roo.Toolbar} 
40319      */
40320     getToolbar : function(){
40321         return this.toolbar;
40322     },
40323     
40324     setActiveState : function(active)
40325     {
40326         this.active = active;
40327         this.setActiveClass(active);
40328         if(!active){
40329             if(this.fireEvent("deactivate", this) === false){
40330                 return false;
40331             }
40332             return true;
40333         }
40334         this.fireEvent("activate", this);
40335         return true;
40336     },
40337     /**
40338      * Updates this panel's element (not for iframe)
40339      * @param {String} content The new content
40340      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40341     */
40342     setContent : function(content, loadScripts){
40343         if (this.iframe) {
40344             return;
40345         }
40346         
40347         this.el.update(content, loadScripts);
40348     },
40349
40350     ignoreResize : function(w, h){
40351         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40352             return true;
40353         }else{
40354             this.lastSize = {width: w, height: h};
40355             return false;
40356         }
40357     },
40358     /**
40359      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40360      * @return {Roo.UpdateManager} The UpdateManager
40361      */
40362     getUpdateManager : function(){
40363         if (this.iframe) {
40364             return false;
40365         }
40366         return this.el.getUpdateManager();
40367     },
40368      /**
40369      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40370      * Does not work with IFRAME contents
40371      * @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:
40372 <pre><code>
40373 panel.load({
40374     url: "your-url.php",
40375     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40376     callback: yourFunction,
40377     scope: yourObject, //(optional scope)
40378     discardUrl: false,
40379     nocache: false,
40380     text: "Loading...",
40381     timeout: 30,
40382     scripts: false
40383 });
40384 </code></pre>
40385      
40386      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40387      * 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.
40388      * @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}
40389      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40390      * @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.
40391      * @return {Roo.ContentPanel} this
40392      */
40393     load : function(){
40394         
40395         if (this.iframe) {
40396             return this;
40397         }
40398         
40399         var um = this.el.getUpdateManager();
40400         um.update.apply(um, arguments);
40401         return this;
40402     },
40403
40404
40405     /**
40406      * 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.
40407      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40408      * @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)
40409      * @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)
40410      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40411      */
40412     setUrl : function(url, params, loadOnce){
40413         if (this.iframe) {
40414             this.iframeEl.dom.src = url;
40415             return false;
40416         }
40417         
40418         if(this.refreshDelegate){
40419             this.removeListener("activate", this.refreshDelegate);
40420         }
40421         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40422         this.on("activate", this.refreshDelegate);
40423         return this.el.getUpdateManager();
40424     },
40425     
40426     _handleRefresh : function(url, params, loadOnce){
40427         if(!loadOnce || !this.loaded){
40428             var updater = this.el.getUpdateManager();
40429             updater.update(url, params, this._setLoaded.createDelegate(this));
40430         }
40431     },
40432     
40433     _setLoaded : function(){
40434         this.loaded = true;
40435     }, 
40436     
40437     /**
40438      * Returns this panel's id
40439      * @return {String} 
40440      */
40441     getId : function(){
40442         return this.el.id;
40443     },
40444     
40445     /** 
40446      * Returns this panel's element - used by regiosn to add.
40447      * @return {Roo.Element} 
40448      */
40449     getEl : function(){
40450         return this.wrapEl || this.el;
40451     },
40452     
40453    
40454     
40455     adjustForComponents : function(width, height)
40456     {
40457         //Roo.log('adjustForComponents ');
40458         if(this.resizeEl != this.el){
40459             width -= this.el.getFrameWidth('lr');
40460             height -= this.el.getFrameWidth('tb');
40461         }
40462         if(this.toolbar){
40463             var te = this.toolbar.getEl();
40464             te.setWidth(width);
40465             height -= te.getHeight();
40466         }
40467         if(this.footer){
40468             var te = this.footer.getEl();
40469             te.setWidth(width);
40470             height -= te.getHeight();
40471         }
40472         
40473         
40474         if(this.adjustments){
40475             width += this.adjustments[0];
40476             height += this.adjustments[1];
40477         }
40478         return {"width": width, "height": height};
40479     },
40480     
40481     setSize : function(width, height){
40482         if(this.fitToFrame && !this.ignoreResize(width, height)){
40483             if(this.fitContainer && this.resizeEl != this.el){
40484                 this.el.setSize(width, height);
40485             }
40486             var size = this.adjustForComponents(width, height);
40487             if (this.iframe) {
40488                 this.iframeEl.setSize(width,height);
40489             }
40490             
40491             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40492             this.fireEvent('resize', this, size.width, size.height);
40493             
40494             
40495         }
40496     },
40497     
40498     /**
40499      * Returns this panel's title
40500      * @return {String} 
40501      */
40502     getTitle : function(){
40503         
40504         if (typeof(this.title) != 'object') {
40505             return this.title;
40506         }
40507         
40508         var t = '';
40509         for (var k in this.title) {
40510             if (!this.title.hasOwnProperty(k)) {
40511                 continue;
40512             }
40513             
40514             if (k.indexOf('-') >= 0) {
40515                 var s = k.split('-');
40516                 for (var i = 0; i<s.length; i++) {
40517                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40518                 }
40519             } else {
40520                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40521             }
40522         }
40523         return t;
40524     },
40525     
40526     /**
40527      * Set this panel's title
40528      * @param {String} title
40529      */
40530     setTitle : function(title){
40531         this.title = title;
40532         if(this.region){
40533             this.region.updatePanelTitle(this, title);
40534         }
40535     },
40536     
40537     /**
40538      * Returns true is this panel was configured to be closable
40539      * @return {Boolean} 
40540      */
40541     isClosable : function(){
40542         return this.closable;
40543     },
40544     
40545     beforeSlide : function(){
40546         this.el.clip();
40547         this.resizeEl.clip();
40548     },
40549     
40550     afterSlide : function(){
40551         this.el.unclip();
40552         this.resizeEl.unclip();
40553     },
40554     
40555     /**
40556      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40557      *   Will fail silently if the {@link #setUrl} method has not been called.
40558      *   This does not activate the panel, just updates its content.
40559      */
40560     refresh : function(){
40561         if(this.refreshDelegate){
40562            this.loaded = false;
40563            this.refreshDelegate();
40564         }
40565     },
40566     
40567     /**
40568      * Destroys this panel
40569      */
40570     destroy : function(){
40571         this.el.removeAllListeners();
40572         var tempEl = document.createElement("span");
40573         tempEl.appendChild(this.el.dom);
40574         tempEl.innerHTML = "";
40575         this.el.remove();
40576         this.el = null;
40577     },
40578     
40579     /**
40580      * form - if the content panel contains a form - this is a reference to it.
40581      * @type {Roo.form.Form}
40582      */
40583     form : false,
40584     /**
40585      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40586      *    This contains a reference to it.
40587      * @type {Roo.View}
40588      */
40589     view : false,
40590     
40591       /**
40592      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40593      * <pre><code>
40594
40595 layout.addxtype({
40596        xtype : 'Form',
40597        items: [ .... ]
40598    }
40599 );
40600
40601 </code></pre>
40602      * @param {Object} cfg Xtype definition of item to add.
40603      */
40604     
40605     
40606     getChildContainer: function () {
40607         return this.getEl();
40608     },
40609     
40610     
40611     onScroll : function(e)
40612     {
40613         this.fireEvent('scroll', this, e);
40614     }
40615     
40616     
40617     /*
40618         var  ret = new Roo.factory(cfg);
40619         return ret;
40620         
40621         
40622         // add form..
40623         if (cfg.xtype.match(/^Form$/)) {
40624             
40625             var el;
40626             //if (this.footer) {
40627             //    el = this.footer.container.insertSibling(false, 'before');
40628             //} else {
40629                 el = this.el.createChild();
40630             //}
40631
40632             this.form = new  Roo.form.Form(cfg);
40633             
40634             
40635             if ( this.form.allItems.length) {
40636                 this.form.render(el.dom);
40637             }
40638             return this.form;
40639         }
40640         // should only have one of theses..
40641         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40642             // views.. should not be just added - used named prop 'view''
40643             
40644             cfg.el = this.el.appendChild(document.createElement("div"));
40645             // factory?
40646             
40647             var ret = new Roo.factory(cfg);
40648              
40649              ret.render && ret.render(false, ''); // render blank..
40650             this.view = ret;
40651             return ret;
40652         }
40653         return false;
40654     }
40655     \*/
40656 });
40657  
40658 /**
40659  * @class Roo.bootstrap.panel.Grid
40660  * @extends Roo.bootstrap.panel.Content
40661  * @constructor
40662  * Create a new GridPanel.
40663  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40664  * @param {Object} config A the config object
40665   
40666  */
40667
40668
40669
40670 Roo.bootstrap.panel.Grid = function(config)
40671 {
40672     
40673       
40674     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40675         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40676
40677     config.el = this.wrapper;
40678     //this.el = this.wrapper;
40679     
40680       if (config.container) {
40681         // ctor'ed from a Border/panel.grid
40682         
40683         
40684         this.wrapper.setStyle("overflow", "hidden");
40685         this.wrapper.addClass('roo-grid-container');
40686
40687     }
40688     
40689     
40690     if(config.toolbar){
40691         var tool_el = this.wrapper.createChild();    
40692         this.toolbar = Roo.factory(config.toolbar);
40693         var ti = [];
40694         if (config.toolbar.items) {
40695             ti = config.toolbar.items ;
40696             delete config.toolbar.items ;
40697         }
40698         
40699         var nitems = [];
40700         this.toolbar.render(tool_el);
40701         for(var i =0;i < ti.length;i++) {
40702           //  Roo.log(['add child', items[i]]);
40703             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40704         }
40705         this.toolbar.items = nitems;
40706         
40707         delete config.toolbar;
40708     }
40709     
40710     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40711     config.grid.scrollBody = true;;
40712     config.grid.monitorWindowResize = false; // turn off autosizing
40713     config.grid.autoHeight = false;
40714     config.grid.autoWidth = false;
40715     
40716     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40717     
40718     if (config.background) {
40719         // render grid on panel activation (if panel background)
40720         this.on('activate', function(gp) {
40721             if (!gp.grid.rendered) {
40722                 gp.grid.render(this.wrapper);
40723                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40724             }
40725         });
40726             
40727     } else {
40728         this.grid.render(this.wrapper);
40729         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40730
40731     }
40732     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40733     // ??? needed ??? config.el = this.wrapper;
40734     
40735     
40736     
40737   
40738     // xtype created footer. - not sure if will work as we normally have to render first..
40739     if (this.footer && !this.footer.el && this.footer.xtype) {
40740         
40741         var ctr = this.grid.getView().getFooterPanel(true);
40742         this.footer.dataSource = this.grid.dataSource;
40743         this.footer = Roo.factory(this.footer, Roo);
40744         this.footer.render(ctr);
40745         
40746     }
40747     
40748     
40749     
40750     
40751      
40752 };
40753
40754 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40755     getId : function(){
40756         return this.grid.id;
40757     },
40758     
40759     /**
40760      * Returns the grid for this panel
40761      * @return {Roo.bootstrap.Table} 
40762      */
40763     getGrid : function(){
40764         return this.grid;    
40765     },
40766     
40767     setSize : function(width, height){
40768         if(!this.ignoreResize(width, height)){
40769             var grid = this.grid;
40770             var size = this.adjustForComponents(width, height);
40771             // tfoot is not a footer?
40772           
40773             
40774             var gridel = grid.getGridEl();
40775             gridel.setSize(size.width, size.height);
40776             
40777             var tbd = grid.getGridEl().select('tbody', true).first();
40778             var thd = grid.getGridEl().select('thead',true).first();
40779             var tbf= grid.getGridEl().select('tfoot', true).first();
40780
40781             if (tbf) {
40782                 size.height -= tbf.getHeight();
40783             }
40784             if (thd) {
40785                 size.height -= thd.getHeight();
40786             }
40787             
40788             tbd.setSize(size.width, size.height );
40789             // this is for the account management tab -seems to work there.
40790             var thd = grid.getGridEl().select('thead',true).first();
40791             //if (tbd) {
40792             //    tbd.setSize(size.width, size.height - thd.getHeight());
40793             //}
40794              
40795             grid.autoSize();
40796         }
40797     },
40798      
40799     
40800     
40801     beforeSlide : function(){
40802         this.grid.getView().scroller.clip();
40803     },
40804     
40805     afterSlide : function(){
40806         this.grid.getView().scroller.unclip();
40807     },
40808     
40809     destroy : function(){
40810         this.grid.destroy();
40811         delete this.grid;
40812         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40813     }
40814 });
40815
40816 /**
40817  * @class Roo.bootstrap.panel.Nest
40818  * @extends Roo.bootstrap.panel.Content
40819  * @constructor
40820  * Create a new Panel, that can contain a layout.Border.
40821  * 
40822  * 
40823  * @param {Roo.BorderLayout} layout The layout for this panel
40824  * @param {String/Object} config A string to set only the title or a config object
40825  */
40826 Roo.bootstrap.panel.Nest = function(config)
40827 {
40828     // construct with only one argument..
40829     /* FIXME - implement nicer consturctors
40830     if (layout.layout) {
40831         config = layout;
40832         layout = config.layout;
40833         delete config.layout;
40834     }
40835     if (layout.xtype && !layout.getEl) {
40836         // then layout needs constructing..
40837         layout = Roo.factory(layout, Roo);
40838     }
40839     */
40840     
40841     config.el =  config.layout.getEl();
40842     
40843     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40844     
40845     config.layout.monitorWindowResize = false; // turn off autosizing
40846     this.layout = config.layout;
40847     this.layout.getEl().addClass("roo-layout-nested-layout");
40848     this.layout.parent = this;
40849     
40850     
40851     
40852     
40853 };
40854
40855 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40856
40857     setSize : function(width, height){
40858         if(!this.ignoreResize(width, height)){
40859             var size = this.adjustForComponents(width, height);
40860             var el = this.layout.getEl();
40861             if (size.height < 1) {
40862                 el.setWidth(size.width);   
40863             } else {
40864                 el.setSize(size.width, size.height);
40865             }
40866             var touch = el.dom.offsetWidth;
40867             this.layout.layout();
40868             // ie requires a double layout on the first pass
40869             if(Roo.isIE && !this.initialized){
40870                 this.initialized = true;
40871                 this.layout.layout();
40872             }
40873         }
40874     },
40875     
40876     // activate all subpanels if not currently active..
40877     
40878     setActiveState : function(active){
40879         this.active = active;
40880         this.setActiveClass(active);
40881         
40882         if(!active){
40883             this.fireEvent("deactivate", this);
40884             return;
40885         }
40886         
40887         this.fireEvent("activate", this);
40888         // not sure if this should happen before or after..
40889         if (!this.layout) {
40890             return; // should not happen..
40891         }
40892         var reg = false;
40893         for (var r in this.layout.regions) {
40894             reg = this.layout.getRegion(r);
40895             if (reg.getActivePanel()) {
40896                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40897                 reg.setActivePanel(reg.getActivePanel());
40898                 continue;
40899             }
40900             if (!reg.panels.length) {
40901                 continue;
40902             }
40903             reg.showPanel(reg.getPanel(0));
40904         }
40905         
40906         
40907         
40908         
40909     },
40910     
40911     /**
40912      * Returns the nested BorderLayout for this panel
40913      * @return {Roo.BorderLayout} 
40914      */
40915     getLayout : function(){
40916         return this.layout;
40917     },
40918     
40919      /**
40920      * Adds a xtype elements to the layout of the nested panel
40921      * <pre><code>
40922
40923 panel.addxtype({
40924        xtype : 'ContentPanel',
40925        region: 'west',
40926        items: [ .... ]
40927    }
40928 );
40929
40930 panel.addxtype({
40931         xtype : 'NestedLayoutPanel',
40932         region: 'west',
40933         layout: {
40934            center: { },
40935            west: { }   
40936         },
40937         items : [ ... list of content panels or nested layout panels.. ]
40938    }
40939 );
40940 </code></pre>
40941      * @param {Object} cfg Xtype definition of item to add.
40942      */
40943     addxtype : function(cfg) {
40944         return this.layout.addxtype(cfg);
40945     
40946     }
40947 });/*
40948  * Based on:
40949  * Ext JS Library 1.1.1
40950  * Copyright(c) 2006-2007, Ext JS, LLC.
40951  *
40952  * Originally Released Under LGPL - original licence link has changed is not relivant.
40953  *
40954  * Fork - LGPL
40955  * <script type="text/javascript">
40956  */
40957 /**
40958  * @class Roo.TabPanel
40959  * @extends Roo.util.Observable
40960  * A lightweight tab container.
40961  * <br><br>
40962  * Usage:
40963  * <pre><code>
40964 // basic tabs 1, built from existing content
40965 var tabs = new Roo.TabPanel("tabs1");
40966 tabs.addTab("script", "View Script");
40967 tabs.addTab("markup", "View Markup");
40968 tabs.activate("script");
40969
40970 // more advanced tabs, built from javascript
40971 var jtabs = new Roo.TabPanel("jtabs");
40972 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40973
40974 // set up the UpdateManager
40975 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40976 var updater = tab2.getUpdateManager();
40977 updater.setDefaultUrl("ajax1.htm");
40978 tab2.on('activate', updater.refresh, updater, true);
40979
40980 // Use setUrl for Ajax loading
40981 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40982 tab3.setUrl("ajax2.htm", null, true);
40983
40984 // Disabled tab
40985 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40986 tab4.disable();
40987
40988 jtabs.activate("jtabs-1");
40989  * </code></pre>
40990  * @constructor
40991  * Create a new TabPanel.
40992  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40993  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40994  */
40995 Roo.bootstrap.panel.Tabs = function(config){
40996     /**
40997     * The container element for this TabPanel.
40998     * @type Roo.Element
40999     */
41000     this.el = Roo.get(config.el);
41001     delete config.el;
41002     if(config){
41003         if(typeof config == "boolean"){
41004             this.tabPosition = config ? "bottom" : "top";
41005         }else{
41006             Roo.apply(this, config);
41007         }
41008     }
41009     
41010     if(this.tabPosition == "bottom"){
41011         // if tabs are at the bottom = create the body first.
41012         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41013         this.el.addClass("roo-tabs-bottom");
41014     }
41015     // next create the tabs holders
41016     
41017     if (this.tabPosition == "west"){
41018         
41019         var reg = this.region; // fake it..
41020         while (reg) {
41021             if (!reg.mgr.parent) {
41022                 break;
41023             }
41024             reg = reg.mgr.parent.region;
41025         }
41026         Roo.log("got nest?");
41027         Roo.log(reg);
41028         if (reg.mgr.getRegion('west')) {
41029             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41030             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41031             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41032             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41033             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41034         
41035             
41036         }
41037         
41038         
41039     } else {
41040      
41041         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41042         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41043         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41044         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41045     }
41046     
41047     
41048     if(Roo.isIE){
41049         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41050     }
41051     
41052     // finally - if tabs are at the top, then create the body last..
41053     if(this.tabPosition != "bottom"){
41054         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41055          * @type Roo.Element
41056          */
41057         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41058         this.el.addClass("roo-tabs-top");
41059     }
41060     this.items = [];
41061
41062     this.bodyEl.setStyle("position", "relative");
41063
41064     this.active = null;
41065     this.activateDelegate = this.activate.createDelegate(this);
41066
41067     this.addEvents({
41068         /**
41069          * @event tabchange
41070          * Fires when the active tab changes
41071          * @param {Roo.TabPanel} this
41072          * @param {Roo.TabPanelItem} activePanel The new active tab
41073          */
41074         "tabchange": true,
41075         /**
41076          * @event beforetabchange
41077          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41078          * @param {Roo.TabPanel} this
41079          * @param {Object} e Set cancel to true on this object to cancel the tab change
41080          * @param {Roo.TabPanelItem} tab The tab being changed to
41081          */
41082         "beforetabchange" : true
41083     });
41084
41085     Roo.EventManager.onWindowResize(this.onResize, this);
41086     this.cpad = this.el.getPadding("lr");
41087     this.hiddenCount = 0;
41088
41089
41090     // toolbar on the tabbar support...
41091     if (this.toolbar) {
41092         alert("no toolbar support yet");
41093         this.toolbar  = false;
41094         /*
41095         var tcfg = this.toolbar;
41096         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41097         this.toolbar = new Roo.Toolbar(tcfg);
41098         if (Roo.isSafari) {
41099             var tbl = tcfg.container.child('table', true);
41100             tbl.setAttribute('width', '100%');
41101         }
41102         */
41103         
41104     }
41105    
41106
41107
41108     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41109 };
41110
41111 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41112     /*
41113      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41114      */
41115     tabPosition : "top",
41116     /*
41117      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41118      */
41119     currentTabWidth : 0,
41120     /*
41121      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41122      */
41123     minTabWidth : 40,
41124     /*
41125      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41126      */
41127     maxTabWidth : 250,
41128     /*
41129      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41130      */
41131     preferredTabWidth : 175,
41132     /*
41133      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41134      */
41135     resizeTabs : false,
41136     /*
41137      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41138      */
41139     monitorResize : true,
41140     /*
41141      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41142      */
41143     toolbar : false,  // set by caller..
41144     
41145     region : false, /// set by caller
41146     
41147     disableTooltips : true, // not used yet...
41148
41149     /**
41150      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41151      * @param {String} id The id of the div to use <b>or create</b>
41152      * @param {String} text The text for the tab
41153      * @param {String} content (optional) Content to put in the TabPanelItem body
41154      * @param {Boolean} closable (optional) True to create a close icon on the tab
41155      * @return {Roo.TabPanelItem} The created TabPanelItem
41156      */
41157     addTab : function(id, text, content, closable, tpl)
41158     {
41159         var item = new Roo.bootstrap.panel.TabItem({
41160             panel: this,
41161             id : id,
41162             text : text,
41163             closable : closable,
41164             tpl : tpl
41165         });
41166         this.addTabItem(item);
41167         if(content){
41168             item.setContent(content);
41169         }
41170         return item;
41171     },
41172
41173     /**
41174      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41175      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41176      * @return {Roo.TabPanelItem}
41177      */
41178     getTab : function(id){
41179         return this.items[id];
41180     },
41181
41182     /**
41183      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41184      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41185      */
41186     hideTab : function(id){
41187         var t = this.items[id];
41188         if(!t.isHidden()){
41189            t.setHidden(true);
41190            this.hiddenCount++;
41191            this.autoSizeTabs();
41192         }
41193     },
41194
41195     /**
41196      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41197      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41198      */
41199     unhideTab : function(id){
41200         var t = this.items[id];
41201         if(t.isHidden()){
41202            t.setHidden(false);
41203            this.hiddenCount--;
41204            this.autoSizeTabs();
41205         }
41206     },
41207
41208     /**
41209      * Adds an existing {@link Roo.TabPanelItem}.
41210      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41211      */
41212     addTabItem : function(item)
41213     {
41214         this.items[item.id] = item;
41215         this.items.push(item);
41216         this.autoSizeTabs();
41217       //  if(this.resizeTabs){
41218     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41219   //         this.autoSizeTabs();
41220 //        }else{
41221 //            item.autoSize();
41222        // }
41223     },
41224
41225     /**
41226      * Removes a {@link Roo.TabPanelItem}.
41227      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41228      */
41229     removeTab : function(id){
41230         var items = this.items;
41231         var tab = items[id];
41232         if(!tab) { return; }
41233         var index = items.indexOf(tab);
41234         if(this.active == tab && items.length > 1){
41235             var newTab = this.getNextAvailable(index);
41236             if(newTab) {
41237                 newTab.activate();
41238             }
41239         }
41240         this.stripEl.dom.removeChild(tab.pnode.dom);
41241         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41242             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41243         }
41244         items.splice(index, 1);
41245         delete this.items[tab.id];
41246         tab.fireEvent("close", tab);
41247         tab.purgeListeners();
41248         this.autoSizeTabs();
41249     },
41250
41251     getNextAvailable : function(start){
41252         var items = this.items;
41253         var index = start;
41254         // look for a next tab that will slide over to
41255         // replace the one being removed
41256         while(index < items.length){
41257             var item = items[++index];
41258             if(item && !item.isHidden()){
41259                 return item;
41260             }
41261         }
41262         // if one isn't found select the previous tab (on the left)
41263         index = start;
41264         while(index >= 0){
41265             var item = items[--index];
41266             if(item && !item.isHidden()){
41267                 return item;
41268             }
41269         }
41270         return null;
41271     },
41272
41273     /**
41274      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41275      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41276      */
41277     disableTab : function(id){
41278         var tab = this.items[id];
41279         if(tab && this.active != tab){
41280             tab.disable();
41281         }
41282     },
41283
41284     /**
41285      * Enables a {@link Roo.TabPanelItem} that is disabled.
41286      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41287      */
41288     enableTab : function(id){
41289         var tab = this.items[id];
41290         tab.enable();
41291     },
41292
41293     /**
41294      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41295      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41296      * @return {Roo.TabPanelItem} The TabPanelItem.
41297      */
41298     activate : function(id)
41299     {
41300         //Roo.log('activite:'  + id);
41301         
41302         var tab = this.items[id];
41303         if(!tab){
41304             return null;
41305         }
41306         if(tab == this.active || tab.disabled){
41307             return tab;
41308         }
41309         var e = {};
41310         this.fireEvent("beforetabchange", this, e, tab);
41311         if(e.cancel !== true && !tab.disabled){
41312             if(this.active){
41313                 this.active.hide();
41314             }
41315             this.active = this.items[id];
41316             this.active.show();
41317             this.fireEvent("tabchange", this, this.active);
41318         }
41319         return tab;
41320     },
41321
41322     /**
41323      * Gets the active {@link Roo.TabPanelItem}.
41324      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41325      */
41326     getActiveTab : function(){
41327         return this.active;
41328     },
41329
41330     /**
41331      * Updates the tab body element to fit the height of the container element
41332      * for overflow scrolling
41333      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41334      */
41335     syncHeight : function(targetHeight){
41336         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41337         var bm = this.bodyEl.getMargins();
41338         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41339         this.bodyEl.setHeight(newHeight);
41340         return newHeight;
41341     },
41342
41343     onResize : function(){
41344         if(this.monitorResize){
41345             this.autoSizeTabs();
41346         }
41347     },
41348
41349     /**
41350      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41351      */
41352     beginUpdate : function(){
41353         this.updating = true;
41354     },
41355
41356     /**
41357      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41358      */
41359     endUpdate : function(){
41360         this.updating = false;
41361         this.autoSizeTabs();
41362     },
41363
41364     /**
41365      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41366      */
41367     autoSizeTabs : function()
41368     {
41369         var count = this.items.length;
41370         var vcount = count - this.hiddenCount;
41371         
41372         if (vcount < 2) {
41373             this.stripEl.hide();
41374         } else {
41375             this.stripEl.show();
41376         }
41377         
41378         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41379             return;
41380         }
41381         
41382         
41383         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41384         var availWidth = Math.floor(w / vcount);
41385         var b = this.stripBody;
41386         if(b.getWidth() > w){
41387             var tabs = this.items;
41388             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41389             if(availWidth < this.minTabWidth){
41390                 /*if(!this.sleft){    // incomplete scrolling code
41391                     this.createScrollButtons();
41392                 }
41393                 this.showScroll();
41394                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41395             }
41396         }else{
41397             if(this.currentTabWidth < this.preferredTabWidth){
41398                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41399             }
41400         }
41401     },
41402
41403     /**
41404      * Returns the number of tabs in this TabPanel.
41405      * @return {Number}
41406      */
41407      getCount : function(){
41408          return this.items.length;
41409      },
41410
41411     /**
41412      * Resizes all the tabs to the passed width
41413      * @param {Number} The new width
41414      */
41415     setTabWidth : function(width){
41416         this.currentTabWidth = width;
41417         for(var i = 0, len = this.items.length; i < len; i++) {
41418                 if(!this.items[i].isHidden()) {
41419                 this.items[i].setWidth(width);
41420             }
41421         }
41422     },
41423
41424     /**
41425      * Destroys this TabPanel
41426      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41427      */
41428     destroy : function(removeEl){
41429         Roo.EventManager.removeResizeListener(this.onResize, this);
41430         for(var i = 0, len = this.items.length; i < len; i++){
41431             this.items[i].purgeListeners();
41432         }
41433         if(removeEl === true){
41434             this.el.update("");
41435             this.el.remove();
41436         }
41437     },
41438     
41439     createStrip : function(container)
41440     {
41441         var strip = document.createElement("nav");
41442         strip.className = Roo.bootstrap.version == 4 ?
41443             "navbar-light bg-light" : 
41444             "navbar navbar-default"; //"x-tabs-wrap";
41445         container.appendChild(strip);
41446         return strip;
41447     },
41448     
41449     createStripList : function(strip)
41450     {
41451         // div wrapper for retard IE
41452         // returns the "tr" element.
41453         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41454         //'<div class="x-tabs-strip-wrap">'+
41455           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41456           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41457         return strip.firstChild; //.firstChild.firstChild.firstChild;
41458     },
41459     createBody : function(container)
41460     {
41461         var body = document.createElement("div");
41462         Roo.id(body, "tab-body");
41463         //Roo.fly(body).addClass("x-tabs-body");
41464         Roo.fly(body).addClass("tab-content");
41465         container.appendChild(body);
41466         return body;
41467     },
41468     createItemBody :function(bodyEl, id){
41469         var body = Roo.getDom(id);
41470         if(!body){
41471             body = document.createElement("div");
41472             body.id = id;
41473         }
41474         //Roo.fly(body).addClass("x-tabs-item-body");
41475         Roo.fly(body).addClass("tab-pane");
41476          bodyEl.insertBefore(body, bodyEl.firstChild);
41477         return body;
41478     },
41479     /** @private */
41480     createStripElements :  function(stripEl, text, closable, tpl)
41481     {
41482         var td = document.createElement("li"); // was td..
41483         td.className = 'nav-item';
41484         
41485         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41486         
41487         
41488         stripEl.appendChild(td);
41489         /*if(closable){
41490             td.className = "x-tabs-closable";
41491             if(!this.closeTpl){
41492                 this.closeTpl = new Roo.Template(
41493                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41494                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41495                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41496                 );
41497             }
41498             var el = this.closeTpl.overwrite(td, {"text": text});
41499             var close = el.getElementsByTagName("div")[0];
41500             var inner = el.getElementsByTagName("em")[0];
41501             return {"el": el, "close": close, "inner": inner};
41502         } else {
41503         */
41504         // not sure what this is..
41505 //            if(!this.tabTpl){
41506                 //this.tabTpl = new Roo.Template(
41507                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41508                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41509                 //);
41510 //                this.tabTpl = new Roo.Template(
41511 //                   '<a href="#">' +
41512 //                   '<span unselectable="on"' +
41513 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41514 //                            ' >{text}</span></a>'
41515 //                );
41516 //                
41517 //            }
41518
41519
41520             var template = tpl || this.tabTpl || false;
41521             
41522             if(!template){
41523                 template =  new Roo.Template(
41524                         Roo.bootstrap.version == 4 ? 
41525                             (
41526                                 '<a class="nav-link" href="#" unselectable="on"' +
41527                                      (this.disableTooltips ? '' : ' title="{text}"') +
41528                                      ' >{text}</a>'
41529                             ) : (
41530                                 '<a class="nav-link" href="#">' +
41531                                 '<span unselectable="on"' +
41532                                          (this.disableTooltips ? '' : ' title="{text}"') +
41533                                     ' >{text}</span></a>'
41534                             )
41535                 );
41536             }
41537             
41538             switch (typeof(template)) {
41539                 case 'object' :
41540                     break;
41541                 case 'string' :
41542                     template = new Roo.Template(template);
41543                     break;
41544                 default :
41545                     break;
41546             }
41547             
41548             var el = template.overwrite(td, {"text": text});
41549             
41550             var inner = el.getElementsByTagName("span")[0];
41551             
41552             return {"el": el, "inner": inner};
41553             
41554     }
41555         
41556     
41557 });
41558
41559 /**
41560  * @class Roo.TabPanelItem
41561  * @extends Roo.util.Observable
41562  * Represents an individual item (tab plus body) in a TabPanel.
41563  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41564  * @param {String} id The id of this TabPanelItem
41565  * @param {String} text The text for the tab of this TabPanelItem
41566  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41567  */
41568 Roo.bootstrap.panel.TabItem = function(config){
41569     /**
41570      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41571      * @type Roo.TabPanel
41572      */
41573     this.tabPanel = config.panel;
41574     /**
41575      * The id for this TabPanelItem
41576      * @type String
41577      */
41578     this.id = config.id;
41579     /** @private */
41580     this.disabled = false;
41581     /** @private */
41582     this.text = config.text;
41583     /** @private */
41584     this.loaded = false;
41585     this.closable = config.closable;
41586
41587     /**
41588      * The body element for this TabPanelItem.
41589      * @type Roo.Element
41590      */
41591     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41592     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41593     this.bodyEl.setStyle("display", "block");
41594     this.bodyEl.setStyle("zoom", "1");
41595     //this.hideAction();
41596
41597     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41598     /** @private */
41599     this.el = Roo.get(els.el);
41600     this.inner = Roo.get(els.inner, true);
41601      this.textEl = Roo.bootstrap.version == 4 ?
41602         this.el : Roo.get(this.el.dom.firstChild, true);
41603
41604     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41605     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41606
41607     
41608 //    this.el.on("mousedown", this.onTabMouseDown, this);
41609     this.el.on("click", this.onTabClick, this);
41610     /** @private */
41611     if(config.closable){
41612         var c = Roo.get(els.close, true);
41613         c.dom.title = this.closeText;
41614         c.addClassOnOver("close-over");
41615         c.on("click", this.closeClick, this);
41616      }
41617
41618     this.addEvents({
41619          /**
41620          * @event activate
41621          * Fires when this tab becomes the active tab.
41622          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41623          * @param {Roo.TabPanelItem} this
41624          */
41625         "activate": true,
41626         /**
41627          * @event beforeclose
41628          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41629          * @param {Roo.TabPanelItem} this
41630          * @param {Object} e Set cancel to true on this object to cancel the close.
41631          */
41632         "beforeclose": true,
41633         /**
41634          * @event close
41635          * Fires when this tab is closed.
41636          * @param {Roo.TabPanelItem} this
41637          */
41638          "close": true,
41639         /**
41640          * @event deactivate
41641          * Fires when this tab is no longer the active tab.
41642          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41643          * @param {Roo.TabPanelItem} this
41644          */
41645          "deactivate" : true
41646     });
41647     this.hidden = false;
41648
41649     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41650 };
41651
41652 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41653            {
41654     purgeListeners : function(){
41655        Roo.util.Observable.prototype.purgeListeners.call(this);
41656        this.el.removeAllListeners();
41657     },
41658     /**
41659      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41660      */
41661     show : function(){
41662         this.status_node.addClass("active");
41663         this.showAction();
41664         if(Roo.isOpera){
41665             this.tabPanel.stripWrap.repaint();
41666         }
41667         this.fireEvent("activate", this.tabPanel, this);
41668     },
41669
41670     /**
41671      * Returns true if this tab is the active tab.
41672      * @return {Boolean}
41673      */
41674     isActive : function(){
41675         return this.tabPanel.getActiveTab() == this;
41676     },
41677
41678     /**
41679      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41680      */
41681     hide : function(){
41682         this.status_node.removeClass("active");
41683         this.hideAction();
41684         this.fireEvent("deactivate", this.tabPanel, this);
41685     },
41686
41687     hideAction : function(){
41688         this.bodyEl.hide();
41689         this.bodyEl.setStyle("position", "absolute");
41690         this.bodyEl.setLeft("-20000px");
41691         this.bodyEl.setTop("-20000px");
41692     },
41693
41694     showAction : function(){
41695         this.bodyEl.setStyle("position", "relative");
41696         this.bodyEl.setTop("");
41697         this.bodyEl.setLeft("");
41698         this.bodyEl.show();
41699     },
41700
41701     /**
41702      * Set the tooltip for the tab.
41703      * @param {String} tooltip The tab's tooltip
41704      */
41705     setTooltip : function(text){
41706         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41707             this.textEl.dom.qtip = text;
41708             this.textEl.dom.removeAttribute('title');
41709         }else{
41710             this.textEl.dom.title = text;
41711         }
41712     },
41713
41714     onTabClick : function(e){
41715         e.preventDefault();
41716         this.tabPanel.activate(this.id);
41717     },
41718
41719     onTabMouseDown : function(e){
41720         e.preventDefault();
41721         this.tabPanel.activate(this.id);
41722     },
41723 /*
41724     getWidth : function(){
41725         return this.inner.getWidth();
41726     },
41727
41728     setWidth : function(width){
41729         var iwidth = width - this.linode.getPadding("lr");
41730         this.inner.setWidth(iwidth);
41731         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41732         this.linode.setWidth(width);
41733     },
41734 */
41735     /**
41736      * Show or hide the tab
41737      * @param {Boolean} hidden True to hide or false to show.
41738      */
41739     setHidden : function(hidden){
41740         this.hidden = hidden;
41741         this.linode.setStyle("display", hidden ? "none" : "");
41742     },
41743
41744     /**
41745      * Returns true if this tab is "hidden"
41746      * @return {Boolean}
41747      */
41748     isHidden : function(){
41749         return this.hidden;
41750     },
41751
41752     /**
41753      * Returns the text for this tab
41754      * @return {String}
41755      */
41756     getText : function(){
41757         return this.text;
41758     },
41759     /*
41760     autoSize : function(){
41761         //this.el.beginMeasure();
41762         this.textEl.setWidth(1);
41763         /*
41764          *  #2804 [new] Tabs in Roojs
41765          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41766          */
41767         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41768         //this.el.endMeasure();
41769     //},
41770
41771     /**
41772      * Sets the text for the tab (Note: this also sets the tooltip text)
41773      * @param {String} text The tab's text and tooltip
41774      */
41775     setText : function(text){
41776         this.text = text;
41777         this.textEl.update(text);
41778         this.setTooltip(text);
41779         //if(!this.tabPanel.resizeTabs){
41780         //    this.autoSize();
41781         //}
41782     },
41783     /**
41784      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41785      */
41786     activate : function(){
41787         this.tabPanel.activate(this.id);
41788     },
41789
41790     /**
41791      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41792      */
41793     disable : function(){
41794         if(this.tabPanel.active != this){
41795             this.disabled = true;
41796             this.status_node.addClass("disabled");
41797         }
41798     },
41799
41800     /**
41801      * Enables this TabPanelItem if it was previously disabled.
41802      */
41803     enable : function(){
41804         this.disabled = false;
41805         this.status_node.removeClass("disabled");
41806     },
41807
41808     /**
41809      * Sets the content for this TabPanelItem.
41810      * @param {String} content The content
41811      * @param {Boolean} loadScripts true to look for and load scripts
41812      */
41813     setContent : function(content, loadScripts){
41814         this.bodyEl.update(content, loadScripts);
41815     },
41816
41817     /**
41818      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41819      * @return {Roo.UpdateManager} The UpdateManager
41820      */
41821     getUpdateManager : function(){
41822         return this.bodyEl.getUpdateManager();
41823     },
41824
41825     /**
41826      * Set a URL to be used to load the content for this TabPanelItem.
41827      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41828      * @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)
41829      * @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)
41830      * @return {Roo.UpdateManager} The UpdateManager
41831      */
41832     setUrl : function(url, params, loadOnce){
41833         if(this.refreshDelegate){
41834             this.un('activate', this.refreshDelegate);
41835         }
41836         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41837         this.on("activate", this.refreshDelegate);
41838         return this.bodyEl.getUpdateManager();
41839     },
41840
41841     /** @private */
41842     _handleRefresh : function(url, params, loadOnce){
41843         if(!loadOnce || !this.loaded){
41844             var updater = this.bodyEl.getUpdateManager();
41845             updater.update(url, params, this._setLoaded.createDelegate(this));
41846         }
41847     },
41848
41849     /**
41850      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41851      *   Will fail silently if the setUrl method has not been called.
41852      *   This does not activate the panel, just updates its content.
41853      */
41854     refresh : function(){
41855         if(this.refreshDelegate){
41856            this.loaded = false;
41857            this.refreshDelegate();
41858         }
41859     },
41860
41861     /** @private */
41862     _setLoaded : function(){
41863         this.loaded = true;
41864     },
41865
41866     /** @private */
41867     closeClick : function(e){
41868         var o = {};
41869         e.stopEvent();
41870         this.fireEvent("beforeclose", this, o);
41871         if(o.cancel !== true){
41872             this.tabPanel.removeTab(this.id);
41873         }
41874     },
41875     /**
41876      * The text displayed in the tooltip for the close icon.
41877      * @type String
41878      */
41879     closeText : "Close this tab"
41880 });
41881 /**
41882 *    This script refer to:
41883 *    Title: International Telephone Input
41884 *    Author: Jack O'Connor
41885 *    Code version:  v12.1.12
41886 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41887 **/
41888
41889 Roo.bootstrap.PhoneInputData = function() {
41890     var d = [
41891       [
41892         "Afghanistan (‫افغانستان‬‎)",
41893         "af",
41894         "93"
41895       ],
41896       [
41897         "Albania (Shqipëri)",
41898         "al",
41899         "355"
41900       ],
41901       [
41902         "Algeria (‫الجزائر‬‎)",
41903         "dz",
41904         "213"
41905       ],
41906       [
41907         "American Samoa",
41908         "as",
41909         "1684"
41910       ],
41911       [
41912         "Andorra",
41913         "ad",
41914         "376"
41915       ],
41916       [
41917         "Angola",
41918         "ao",
41919         "244"
41920       ],
41921       [
41922         "Anguilla",
41923         "ai",
41924         "1264"
41925       ],
41926       [
41927         "Antigua and Barbuda",
41928         "ag",
41929         "1268"
41930       ],
41931       [
41932         "Argentina",
41933         "ar",
41934         "54"
41935       ],
41936       [
41937         "Armenia (Հայաստան)",
41938         "am",
41939         "374"
41940       ],
41941       [
41942         "Aruba",
41943         "aw",
41944         "297"
41945       ],
41946       [
41947         "Australia",
41948         "au",
41949         "61",
41950         0
41951       ],
41952       [
41953         "Austria (Österreich)",
41954         "at",
41955         "43"
41956       ],
41957       [
41958         "Azerbaijan (Azərbaycan)",
41959         "az",
41960         "994"
41961       ],
41962       [
41963         "Bahamas",
41964         "bs",
41965         "1242"
41966       ],
41967       [
41968         "Bahrain (‫البحرين‬‎)",
41969         "bh",
41970         "973"
41971       ],
41972       [
41973         "Bangladesh (বাংলাদেশ)",
41974         "bd",
41975         "880"
41976       ],
41977       [
41978         "Barbados",
41979         "bb",
41980         "1246"
41981       ],
41982       [
41983         "Belarus (Беларусь)",
41984         "by",
41985         "375"
41986       ],
41987       [
41988         "Belgium (België)",
41989         "be",
41990         "32"
41991       ],
41992       [
41993         "Belize",
41994         "bz",
41995         "501"
41996       ],
41997       [
41998         "Benin (Bénin)",
41999         "bj",
42000         "229"
42001       ],
42002       [
42003         "Bermuda",
42004         "bm",
42005         "1441"
42006       ],
42007       [
42008         "Bhutan (འབྲུག)",
42009         "bt",
42010         "975"
42011       ],
42012       [
42013         "Bolivia",
42014         "bo",
42015         "591"
42016       ],
42017       [
42018         "Bosnia and Herzegovina (Босна и Херцеговина)",
42019         "ba",
42020         "387"
42021       ],
42022       [
42023         "Botswana",
42024         "bw",
42025         "267"
42026       ],
42027       [
42028         "Brazil (Brasil)",
42029         "br",
42030         "55"
42031       ],
42032       [
42033         "British Indian Ocean Territory",
42034         "io",
42035         "246"
42036       ],
42037       [
42038         "British Virgin Islands",
42039         "vg",
42040         "1284"
42041       ],
42042       [
42043         "Brunei",
42044         "bn",
42045         "673"
42046       ],
42047       [
42048         "Bulgaria (България)",
42049         "bg",
42050         "359"
42051       ],
42052       [
42053         "Burkina Faso",
42054         "bf",
42055         "226"
42056       ],
42057       [
42058         "Burundi (Uburundi)",
42059         "bi",
42060         "257"
42061       ],
42062       [
42063         "Cambodia (កម្ពុជា)",
42064         "kh",
42065         "855"
42066       ],
42067       [
42068         "Cameroon (Cameroun)",
42069         "cm",
42070         "237"
42071       ],
42072       [
42073         "Canada",
42074         "ca",
42075         "1",
42076         1,
42077         ["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"]
42078       ],
42079       [
42080         "Cape Verde (Kabu Verdi)",
42081         "cv",
42082         "238"
42083       ],
42084       [
42085         "Caribbean Netherlands",
42086         "bq",
42087         "599",
42088         1
42089       ],
42090       [
42091         "Cayman Islands",
42092         "ky",
42093         "1345"
42094       ],
42095       [
42096         "Central African Republic (République centrafricaine)",
42097         "cf",
42098         "236"
42099       ],
42100       [
42101         "Chad (Tchad)",
42102         "td",
42103         "235"
42104       ],
42105       [
42106         "Chile",
42107         "cl",
42108         "56"
42109       ],
42110       [
42111         "China (中国)",
42112         "cn",
42113         "86"
42114       ],
42115       [
42116         "Christmas Island",
42117         "cx",
42118         "61",
42119         2
42120       ],
42121       [
42122         "Cocos (Keeling) Islands",
42123         "cc",
42124         "61",
42125         1
42126       ],
42127       [
42128         "Colombia",
42129         "co",
42130         "57"
42131       ],
42132       [
42133         "Comoros (‫جزر القمر‬‎)",
42134         "km",
42135         "269"
42136       ],
42137       [
42138         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42139         "cd",
42140         "243"
42141       ],
42142       [
42143         "Congo (Republic) (Congo-Brazzaville)",
42144         "cg",
42145         "242"
42146       ],
42147       [
42148         "Cook Islands",
42149         "ck",
42150         "682"
42151       ],
42152       [
42153         "Costa Rica",
42154         "cr",
42155         "506"
42156       ],
42157       [
42158         "Côte d’Ivoire",
42159         "ci",
42160         "225"
42161       ],
42162       [
42163         "Croatia (Hrvatska)",
42164         "hr",
42165         "385"
42166       ],
42167       [
42168         "Cuba",
42169         "cu",
42170         "53"
42171       ],
42172       [
42173         "Curaçao",
42174         "cw",
42175         "599",
42176         0
42177       ],
42178       [
42179         "Cyprus (Κύπρος)",
42180         "cy",
42181         "357"
42182       ],
42183       [
42184         "Czech Republic (Česká republika)",
42185         "cz",
42186         "420"
42187       ],
42188       [
42189         "Denmark (Danmark)",
42190         "dk",
42191         "45"
42192       ],
42193       [
42194         "Djibouti",
42195         "dj",
42196         "253"
42197       ],
42198       [
42199         "Dominica",
42200         "dm",
42201         "1767"
42202       ],
42203       [
42204         "Dominican Republic (República Dominicana)",
42205         "do",
42206         "1",
42207         2,
42208         ["809", "829", "849"]
42209       ],
42210       [
42211         "Ecuador",
42212         "ec",
42213         "593"
42214       ],
42215       [
42216         "Egypt (‫مصر‬‎)",
42217         "eg",
42218         "20"
42219       ],
42220       [
42221         "El Salvador",
42222         "sv",
42223         "503"
42224       ],
42225       [
42226         "Equatorial Guinea (Guinea Ecuatorial)",
42227         "gq",
42228         "240"
42229       ],
42230       [
42231         "Eritrea",
42232         "er",
42233         "291"
42234       ],
42235       [
42236         "Estonia (Eesti)",
42237         "ee",
42238         "372"
42239       ],
42240       [
42241         "Ethiopia",
42242         "et",
42243         "251"
42244       ],
42245       [
42246         "Falkland Islands (Islas Malvinas)",
42247         "fk",
42248         "500"
42249       ],
42250       [
42251         "Faroe Islands (Føroyar)",
42252         "fo",
42253         "298"
42254       ],
42255       [
42256         "Fiji",
42257         "fj",
42258         "679"
42259       ],
42260       [
42261         "Finland (Suomi)",
42262         "fi",
42263         "358",
42264         0
42265       ],
42266       [
42267         "France",
42268         "fr",
42269         "33"
42270       ],
42271       [
42272         "French Guiana (Guyane française)",
42273         "gf",
42274         "594"
42275       ],
42276       [
42277         "French Polynesia (Polynésie française)",
42278         "pf",
42279         "689"
42280       ],
42281       [
42282         "Gabon",
42283         "ga",
42284         "241"
42285       ],
42286       [
42287         "Gambia",
42288         "gm",
42289         "220"
42290       ],
42291       [
42292         "Georgia (საქართველო)",
42293         "ge",
42294         "995"
42295       ],
42296       [
42297         "Germany (Deutschland)",
42298         "de",
42299         "49"
42300       ],
42301       [
42302         "Ghana (Gaana)",
42303         "gh",
42304         "233"
42305       ],
42306       [
42307         "Gibraltar",
42308         "gi",
42309         "350"
42310       ],
42311       [
42312         "Greece (Ελλάδα)",
42313         "gr",
42314         "30"
42315       ],
42316       [
42317         "Greenland (Kalaallit Nunaat)",
42318         "gl",
42319         "299"
42320       ],
42321       [
42322         "Grenada",
42323         "gd",
42324         "1473"
42325       ],
42326       [
42327         "Guadeloupe",
42328         "gp",
42329         "590",
42330         0
42331       ],
42332       [
42333         "Guam",
42334         "gu",
42335         "1671"
42336       ],
42337       [
42338         "Guatemala",
42339         "gt",
42340         "502"
42341       ],
42342       [
42343         "Guernsey",
42344         "gg",
42345         "44",
42346         1
42347       ],
42348       [
42349         "Guinea (Guinée)",
42350         "gn",
42351         "224"
42352       ],
42353       [
42354         "Guinea-Bissau (Guiné Bissau)",
42355         "gw",
42356         "245"
42357       ],
42358       [
42359         "Guyana",
42360         "gy",
42361         "592"
42362       ],
42363       [
42364         "Haiti",
42365         "ht",
42366         "509"
42367       ],
42368       [
42369         "Honduras",
42370         "hn",
42371         "504"
42372       ],
42373       [
42374         "Hong Kong (香港)",
42375         "hk",
42376         "852"
42377       ],
42378       [
42379         "Hungary (Magyarország)",
42380         "hu",
42381         "36"
42382       ],
42383       [
42384         "Iceland (Ísland)",
42385         "is",
42386         "354"
42387       ],
42388       [
42389         "India (भारत)",
42390         "in",
42391         "91"
42392       ],
42393       [
42394         "Indonesia",
42395         "id",
42396         "62"
42397       ],
42398       [
42399         "Iran (‫ایران‬‎)",
42400         "ir",
42401         "98"
42402       ],
42403       [
42404         "Iraq (‫العراق‬‎)",
42405         "iq",
42406         "964"
42407       ],
42408       [
42409         "Ireland",
42410         "ie",
42411         "353"
42412       ],
42413       [
42414         "Isle of Man",
42415         "im",
42416         "44",
42417         2
42418       ],
42419       [
42420         "Israel (‫ישראל‬‎)",
42421         "il",
42422         "972"
42423       ],
42424       [
42425         "Italy (Italia)",
42426         "it",
42427         "39",
42428         0
42429       ],
42430       [
42431         "Jamaica",
42432         "jm",
42433         "1876"
42434       ],
42435       [
42436         "Japan (日本)",
42437         "jp",
42438         "81"
42439       ],
42440       [
42441         "Jersey",
42442         "je",
42443         "44",
42444         3
42445       ],
42446       [
42447         "Jordan (‫الأردن‬‎)",
42448         "jo",
42449         "962"
42450       ],
42451       [
42452         "Kazakhstan (Казахстан)",
42453         "kz",
42454         "7",
42455         1
42456       ],
42457       [
42458         "Kenya",
42459         "ke",
42460         "254"
42461       ],
42462       [
42463         "Kiribati",
42464         "ki",
42465         "686"
42466       ],
42467       [
42468         "Kosovo",
42469         "xk",
42470         "383"
42471       ],
42472       [
42473         "Kuwait (‫الكويت‬‎)",
42474         "kw",
42475         "965"
42476       ],
42477       [
42478         "Kyrgyzstan (Кыргызстан)",
42479         "kg",
42480         "996"
42481       ],
42482       [
42483         "Laos (ລາວ)",
42484         "la",
42485         "856"
42486       ],
42487       [
42488         "Latvia (Latvija)",
42489         "lv",
42490         "371"
42491       ],
42492       [
42493         "Lebanon (‫لبنان‬‎)",
42494         "lb",
42495         "961"
42496       ],
42497       [
42498         "Lesotho",
42499         "ls",
42500         "266"
42501       ],
42502       [
42503         "Liberia",
42504         "lr",
42505         "231"
42506       ],
42507       [
42508         "Libya (‫ليبيا‬‎)",
42509         "ly",
42510         "218"
42511       ],
42512       [
42513         "Liechtenstein",
42514         "li",
42515         "423"
42516       ],
42517       [
42518         "Lithuania (Lietuva)",
42519         "lt",
42520         "370"
42521       ],
42522       [
42523         "Luxembourg",
42524         "lu",
42525         "352"
42526       ],
42527       [
42528         "Macau (澳門)",
42529         "mo",
42530         "853"
42531       ],
42532       [
42533         "Macedonia (FYROM) (Македонија)",
42534         "mk",
42535         "389"
42536       ],
42537       [
42538         "Madagascar (Madagasikara)",
42539         "mg",
42540         "261"
42541       ],
42542       [
42543         "Malawi",
42544         "mw",
42545         "265"
42546       ],
42547       [
42548         "Malaysia",
42549         "my",
42550         "60"
42551       ],
42552       [
42553         "Maldives",
42554         "mv",
42555         "960"
42556       ],
42557       [
42558         "Mali",
42559         "ml",
42560         "223"
42561       ],
42562       [
42563         "Malta",
42564         "mt",
42565         "356"
42566       ],
42567       [
42568         "Marshall Islands",
42569         "mh",
42570         "692"
42571       ],
42572       [
42573         "Martinique",
42574         "mq",
42575         "596"
42576       ],
42577       [
42578         "Mauritania (‫موريتانيا‬‎)",
42579         "mr",
42580         "222"
42581       ],
42582       [
42583         "Mauritius (Moris)",
42584         "mu",
42585         "230"
42586       ],
42587       [
42588         "Mayotte",
42589         "yt",
42590         "262",
42591         1
42592       ],
42593       [
42594         "Mexico (México)",
42595         "mx",
42596         "52"
42597       ],
42598       [
42599         "Micronesia",
42600         "fm",
42601         "691"
42602       ],
42603       [
42604         "Moldova (Republica Moldova)",
42605         "md",
42606         "373"
42607       ],
42608       [
42609         "Monaco",
42610         "mc",
42611         "377"
42612       ],
42613       [
42614         "Mongolia (Монгол)",
42615         "mn",
42616         "976"
42617       ],
42618       [
42619         "Montenegro (Crna Gora)",
42620         "me",
42621         "382"
42622       ],
42623       [
42624         "Montserrat",
42625         "ms",
42626         "1664"
42627       ],
42628       [
42629         "Morocco (‫المغرب‬‎)",
42630         "ma",
42631         "212",
42632         0
42633       ],
42634       [
42635         "Mozambique (Moçambique)",
42636         "mz",
42637         "258"
42638       ],
42639       [
42640         "Myanmar (Burma) (မြန်မာ)",
42641         "mm",
42642         "95"
42643       ],
42644       [
42645         "Namibia (Namibië)",
42646         "na",
42647         "264"
42648       ],
42649       [
42650         "Nauru",
42651         "nr",
42652         "674"
42653       ],
42654       [
42655         "Nepal (नेपाल)",
42656         "np",
42657         "977"
42658       ],
42659       [
42660         "Netherlands (Nederland)",
42661         "nl",
42662         "31"
42663       ],
42664       [
42665         "New Caledonia (Nouvelle-Calédonie)",
42666         "nc",
42667         "687"
42668       ],
42669       [
42670         "New Zealand",
42671         "nz",
42672         "64"
42673       ],
42674       [
42675         "Nicaragua",
42676         "ni",
42677         "505"
42678       ],
42679       [
42680         "Niger (Nijar)",
42681         "ne",
42682         "227"
42683       ],
42684       [
42685         "Nigeria",
42686         "ng",
42687         "234"
42688       ],
42689       [
42690         "Niue",
42691         "nu",
42692         "683"
42693       ],
42694       [
42695         "Norfolk Island",
42696         "nf",
42697         "672"
42698       ],
42699       [
42700         "North Korea (조선 민주주의 인민 공화국)",
42701         "kp",
42702         "850"
42703       ],
42704       [
42705         "Northern Mariana Islands",
42706         "mp",
42707         "1670"
42708       ],
42709       [
42710         "Norway (Norge)",
42711         "no",
42712         "47",
42713         0
42714       ],
42715       [
42716         "Oman (‫عُمان‬‎)",
42717         "om",
42718         "968"
42719       ],
42720       [
42721         "Pakistan (‫پاکستان‬‎)",
42722         "pk",
42723         "92"
42724       ],
42725       [
42726         "Palau",
42727         "pw",
42728         "680"
42729       ],
42730       [
42731         "Palestine (‫فلسطين‬‎)",
42732         "ps",
42733         "970"
42734       ],
42735       [
42736         "Panama (Panamá)",
42737         "pa",
42738         "507"
42739       ],
42740       [
42741         "Papua New Guinea",
42742         "pg",
42743         "675"
42744       ],
42745       [
42746         "Paraguay",
42747         "py",
42748         "595"
42749       ],
42750       [
42751         "Peru (Perú)",
42752         "pe",
42753         "51"
42754       ],
42755       [
42756         "Philippines",
42757         "ph",
42758         "63"
42759       ],
42760       [
42761         "Poland (Polska)",
42762         "pl",
42763         "48"
42764       ],
42765       [
42766         "Portugal",
42767         "pt",
42768         "351"
42769       ],
42770       [
42771         "Puerto Rico",
42772         "pr",
42773         "1",
42774         3,
42775         ["787", "939"]
42776       ],
42777       [
42778         "Qatar (‫قطر‬‎)",
42779         "qa",
42780         "974"
42781       ],
42782       [
42783         "Réunion (La Réunion)",
42784         "re",
42785         "262",
42786         0
42787       ],
42788       [
42789         "Romania (România)",
42790         "ro",
42791         "40"
42792       ],
42793       [
42794         "Russia (Россия)",
42795         "ru",
42796         "7",
42797         0
42798       ],
42799       [
42800         "Rwanda",
42801         "rw",
42802         "250"
42803       ],
42804       [
42805         "Saint Barthélemy",
42806         "bl",
42807         "590",
42808         1
42809       ],
42810       [
42811         "Saint Helena",
42812         "sh",
42813         "290"
42814       ],
42815       [
42816         "Saint Kitts and Nevis",
42817         "kn",
42818         "1869"
42819       ],
42820       [
42821         "Saint Lucia",
42822         "lc",
42823         "1758"
42824       ],
42825       [
42826         "Saint Martin (Saint-Martin (partie française))",
42827         "mf",
42828         "590",
42829         2
42830       ],
42831       [
42832         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42833         "pm",
42834         "508"
42835       ],
42836       [
42837         "Saint Vincent and the Grenadines",
42838         "vc",
42839         "1784"
42840       ],
42841       [
42842         "Samoa",
42843         "ws",
42844         "685"
42845       ],
42846       [
42847         "San Marino",
42848         "sm",
42849         "378"
42850       ],
42851       [
42852         "São Tomé and Príncipe (São Tomé e Príncipe)",
42853         "st",
42854         "239"
42855       ],
42856       [
42857         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42858         "sa",
42859         "966"
42860       ],
42861       [
42862         "Senegal (Sénégal)",
42863         "sn",
42864         "221"
42865       ],
42866       [
42867         "Serbia (Србија)",
42868         "rs",
42869         "381"
42870       ],
42871       [
42872         "Seychelles",
42873         "sc",
42874         "248"
42875       ],
42876       [
42877         "Sierra Leone",
42878         "sl",
42879         "232"
42880       ],
42881       [
42882         "Singapore",
42883         "sg",
42884         "65"
42885       ],
42886       [
42887         "Sint Maarten",
42888         "sx",
42889         "1721"
42890       ],
42891       [
42892         "Slovakia (Slovensko)",
42893         "sk",
42894         "421"
42895       ],
42896       [
42897         "Slovenia (Slovenija)",
42898         "si",
42899         "386"
42900       ],
42901       [
42902         "Solomon Islands",
42903         "sb",
42904         "677"
42905       ],
42906       [
42907         "Somalia (Soomaaliya)",
42908         "so",
42909         "252"
42910       ],
42911       [
42912         "South Africa",
42913         "za",
42914         "27"
42915       ],
42916       [
42917         "South Korea (대한민국)",
42918         "kr",
42919         "82"
42920       ],
42921       [
42922         "South Sudan (‫جنوب السودان‬‎)",
42923         "ss",
42924         "211"
42925       ],
42926       [
42927         "Spain (España)",
42928         "es",
42929         "34"
42930       ],
42931       [
42932         "Sri Lanka (ශ්‍රී ලංකාව)",
42933         "lk",
42934         "94"
42935       ],
42936       [
42937         "Sudan (‫السودان‬‎)",
42938         "sd",
42939         "249"
42940       ],
42941       [
42942         "Suriname",
42943         "sr",
42944         "597"
42945       ],
42946       [
42947         "Svalbard and Jan Mayen",
42948         "sj",
42949         "47",
42950         1
42951       ],
42952       [
42953         "Swaziland",
42954         "sz",
42955         "268"
42956       ],
42957       [
42958         "Sweden (Sverige)",
42959         "se",
42960         "46"
42961       ],
42962       [
42963         "Switzerland (Schweiz)",
42964         "ch",
42965         "41"
42966       ],
42967       [
42968         "Syria (‫سوريا‬‎)",
42969         "sy",
42970         "963"
42971       ],
42972       [
42973         "Taiwan (台灣)",
42974         "tw",
42975         "886"
42976       ],
42977       [
42978         "Tajikistan",
42979         "tj",
42980         "992"
42981       ],
42982       [
42983         "Tanzania",
42984         "tz",
42985         "255"
42986       ],
42987       [
42988         "Thailand (ไทย)",
42989         "th",
42990         "66"
42991       ],
42992       [
42993         "Timor-Leste",
42994         "tl",
42995         "670"
42996       ],
42997       [
42998         "Togo",
42999         "tg",
43000         "228"
43001       ],
43002       [
43003         "Tokelau",
43004         "tk",
43005         "690"
43006       ],
43007       [
43008         "Tonga",
43009         "to",
43010         "676"
43011       ],
43012       [
43013         "Trinidad and Tobago",
43014         "tt",
43015         "1868"
43016       ],
43017       [
43018         "Tunisia (‫تونس‬‎)",
43019         "tn",
43020         "216"
43021       ],
43022       [
43023         "Turkey (Türkiye)",
43024         "tr",
43025         "90"
43026       ],
43027       [
43028         "Turkmenistan",
43029         "tm",
43030         "993"
43031       ],
43032       [
43033         "Turks and Caicos Islands",
43034         "tc",
43035         "1649"
43036       ],
43037       [
43038         "Tuvalu",
43039         "tv",
43040         "688"
43041       ],
43042       [
43043         "U.S. Virgin Islands",
43044         "vi",
43045         "1340"
43046       ],
43047       [
43048         "Uganda",
43049         "ug",
43050         "256"
43051       ],
43052       [
43053         "Ukraine (Україна)",
43054         "ua",
43055         "380"
43056       ],
43057       [
43058         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43059         "ae",
43060         "971"
43061       ],
43062       [
43063         "United Kingdom",
43064         "gb",
43065         "44",
43066         0
43067       ],
43068       [
43069         "United States",
43070         "us",
43071         "1",
43072         0
43073       ],
43074       [
43075         "Uruguay",
43076         "uy",
43077         "598"
43078       ],
43079       [
43080         "Uzbekistan (Oʻzbekiston)",
43081         "uz",
43082         "998"
43083       ],
43084       [
43085         "Vanuatu",
43086         "vu",
43087         "678"
43088       ],
43089       [
43090         "Vatican City (Città del Vaticano)",
43091         "va",
43092         "39",
43093         1
43094       ],
43095       [
43096         "Venezuela",
43097         "ve",
43098         "58"
43099       ],
43100       [
43101         "Vietnam (Việt Nam)",
43102         "vn",
43103         "84"
43104       ],
43105       [
43106         "Wallis and Futuna (Wallis-et-Futuna)",
43107         "wf",
43108         "681"
43109       ],
43110       [
43111         "Western Sahara (‫الصحراء الغربية‬‎)",
43112         "eh",
43113         "212",
43114         1
43115       ],
43116       [
43117         "Yemen (‫اليمن‬‎)",
43118         "ye",
43119         "967"
43120       ],
43121       [
43122         "Zambia",
43123         "zm",
43124         "260"
43125       ],
43126       [
43127         "Zimbabwe",
43128         "zw",
43129         "263"
43130       ],
43131       [
43132         "Åland Islands",
43133         "ax",
43134         "358",
43135         1
43136       ]
43137   ];
43138   
43139   return d;
43140 }/**
43141 *    This script refer to:
43142 *    Title: International Telephone Input
43143 *    Author: Jack O'Connor
43144 *    Code version:  v12.1.12
43145 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43146 **/
43147
43148 /**
43149  * @class Roo.bootstrap.PhoneInput
43150  * @extends Roo.bootstrap.TriggerField
43151  * An input with International dial-code selection
43152  
43153  * @cfg {String} defaultDialCode default '+852'
43154  * @cfg {Array} preferedCountries default []
43155   
43156  * @constructor
43157  * Create a new PhoneInput.
43158  * @param {Object} config Configuration options
43159  */
43160
43161 Roo.bootstrap.PhoneInput = function(config) {
43162     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43163 };
43164
43165 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43166         
43167         listWidth: undefined,
43168         
43169         selectedClass: 'active',
43170         
43171         invalidClass : "has-warning",
43172         
43173         validClass: 'has-success',
43174         
43175         allowed: '0123456789',
43176         
43177         max_length: 15,
43178         
43179         /**
43180          * @cfg {String} defaultDialCode The default dial code when initializing the input
43181          */
43182         defaultDialCode: '+852',
43183         
43184         /**
43185          * @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
43186          */
43187         preferedCountries: false,
43188         
43189         getAutoCreate : function()
43190         {
43191             var data = Roo.bootstrap.PhoneInputData();
43192             var align = this.labelAlign || this.parentLabelAlign();
43193             var id = Roo.id();
43194             
43195             this.allCountries = [];
43196             this.dialCodeMapping = [];
43197             
43198             for (var i = 0; i < data.length; i++) {
43199               var c = data[i];
43200               this.allCountries[i] = {
43201                 name: c[0],
43202                 iso2: c[1],
43203                 dialCode: c[2],
43204                 priority: c[3] || 0,
43205                 areaCodes: c[4] || null
43206               };
43207               this.dialCodeMapping[c[2]] = {
43208                   name: c[0],
43209                   iso2: c[1],
43210                   priority: c[3] || 0,
43211                   areaCodes: c[4] || null
43212               };
43213             }
43214             
43215             var cfg = {
43216                 cls: 'form-group',
43217                 cn: []
43218             };
43219             
43220             var input =  {
43221                 tag: 'input',
43222                 id : id,
43223                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43224                 maxlength: this.max_length,
43225                 cls : 'form-control tel-input',
43226                 autocomplete: 'new-password'
43227             };
43228             
43229             var hiddenInput = {
43230                 tag: 'input',
43231                 type: 'hidden',
43232                 cls: 'hidden-tel-input'
43233             };
43234             
43235             if (this.name) {
43236                 hiddenInput.name = this.name;
43237             }
43238             
43239             if (this.disabled) {
43240                 input.disabled = true;
43241             }
43242             
43243             var flag_container = {
43244                 tag: 'div',
43245                 cls: 'flag-box',
43246                 cn: [
43247                     {
43248                         tag: 'div',
43249                         cls: 'flag'
43250                     },
43251                     {
43252                         tag: 'div',
43253                         cls: 'caret'
43254                     }
43255                 ]
43256             };
43257             
43258             var box = {
43259                 tag: 'div',
43260                 cls: this.hasFeedback ? 'has-feedback' : '',
43261                 cn: [
43262                     hiddenInput,
43263                     input,
43264                     {
43265                         tag: 'input',
43266                         cls: 'dial-code-holder',
43267                         disabled: true
43268                     }
43269                 ]
43270             };
43271             
43272             var container = {
43273                 cls: 'roo-select2-container input-group',
43274                 cn: [
43275                     flag_container,
43276                     box
43277                 ]
43278             };
43279             
43280             if (this.fieldLabel.length) {
43281                 var indicator = {
43282                     tag: 'i',
43283                     tooltip: 'This field is required'
43284                 };
43285                 
43286                 var label = {
43287                     tag: 'label',
43288                     'for':  id,
43289                     cls: 'control-label',
43290                     cn: []
43291                 };
43292                 
43293                 var label_text = {
43294                     tag: 'span',
43295                     html: this.fieldLabel
43296                 };
43297                 
43298                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43299                 label.cn = [
43300                     indicator,
43301                     label_text
43302                 ];
43303                 
43304                 if(this.indicatorpos == 'right') {
43305                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43306                     label.cn = [
43307                         label_text,
43308                         indicator
43309                     ];
43310                 }
43311                 
43312                 if(align == 'left') {
43313                     container = {
43314                         tag: 'div',
43315                         cn: [
43316                             container
43317                         ]
43318                     };
43319                     
43320                     if(this.labelWidth > 12){
43321                         label.style = "width: " + this.labelWidth + 'px';
43322                     }
43323                     if(this.labelWidth < 13 && this.labelmd == 0){
43324                         this.labelmd = this.labelWidth;
43325                     }
43326                     if(this.labellg > 0){
43327                         label.cls += ' col-lg-' + this.labellg;
43328                         input.cls += ' col-lg-' + (12 - this.labellg);
43329                     }
43330                     if(this.labelmd > 0){
43331                         label.cls += ' col-md-' + this.labelmd;
43332                         container.cls += ' col-md-' + (12 - this.labelmd);
43333                     }
43334                     if(this.labelsm > 0){
43335                         label.cls += ' col-sm-' + this.labelsm;
43336                         container.cls += ' col-sm-' + (12 - this.labelsm);
43337                     }
43338                     if(this.labelxs > 0){
43339                         label.cls += ' col-xs-' + this.labelxs;
43340                         container.cls += ' col-xs-' + (12 - this.labelxs);
43341                     }
43342                 }
43343             }
43344             
43345             cfg.cn = [
43346                 label,
43347                 container
43348             ];
43349             
43350             var settings = this;
43351             
43352             ['xs','sm','md','lg'].map(function(size){
43353                 if (settings[size]) {
43354                     cfg.cls += ' col-' + size + '-' + settings[size];
43355                 }
43356             });
43357             
43358             this.store = new Roo.data.Store({
43359                 proxy : new Roo.data.MemoryProxy({}),
43360                 reader : new Roo.data.JsonReader({
43361                     fields : [
43362                         {
43363                             'name' : 'name',
43364                             'type' : 'string'
43365                         },
43366                         {
43367                             'name' : 'iso2',
43368                             'type' : 'string'
43369                         },
43370                         {
43371                             'name' : 'dialCode',
43372                             'type' : 'string'
43373                         },
43374                         {
43375                             'name' : 'priority',
43376                             'type' : 'string'
43377                         },
43378                         {
43379                             'name' : 'areaCodes',
43380                             'type' : 'string'
43381                         }
43382                     ]
43383                 })
43384             });
43385             
43386             if(!this.preferedCountries) {
43387                 this.preferedCountries = [
43388                     'hk',
43389                     'gb',
43390                     'us'
43391                 ];
43392             }
43393             
43394             var p = this.preferedCountries.reverse();
43395             
43396             if(p) {
43397                 for (var i = 0; i < p.length; i++) {
43398                     for (var j = 0; j < this.allCountries.length; j++) {
43399                         if(this.allCountries[j].iso2 == p[i]) {
43400                             var t = this.allCountries[j];
43401                             this.allCountries.splice(j,1);
43402                             this.allCountries.unshift(t);
43403                         }
43404                     } 
43405                 }
43406             }
43407             
43408             this.store.proxy.data = {
43409                 success: true,
43410                 data: this.allCountries
43411             };
43412             
43413             return cfg;
43414         },
43415         
43416         initEvents : function()
43417         {
43418             this.createList();
43419             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43420             
43421             this.indicator = this.indicatorEl();
43422             this.flag = this.flagEl();
43423             this.dialCodeHolder = this.dialCodeHolderEl();
43424             
43425             this.trigger = this.el.select('div.flag-box',true).first();
43426             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43427             
43428             var _this = this;
43429             
43430             (function(){
43431                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43432                 _this.list.setWidth(lw);
43433             }).defer(100);
43434             
43435             this.list.on('mouseover', this.onViewOver, this);
43436             this.list.on('mousemove', this.onViewMove, this);
43437             this.inputEl().on("keyup", this.onKeyUp, this);
43438             this.inputEl().on("keypress", this.onKeyPress, this);
43439             
43440             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43441
43442             this.view = new Roo.View(this.list, this.tpl, {
43443                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43444             });
43445             
43446             this.view.on('click', this.onViewClick, this);
43447             this.setValue(this.defaultDialCode);
43448         },
43449         
43450         onTriggerClick : function(e)
43451         {
43452             Roo.log('trigger click');
43453             if(this.disabled){
43454                 return;
43455             }
43456             
43457             if(this.isExpanded()){
43458                 this.collapse();
43459                 this.hasFocus = false;
43460             }else {
43461                 this.store.load({});
43462                 this.hasFocus = true;
43463                 this.expand();
43464             }
43465         },
43466         
43467         isExpanded : function()
43468         {
43469             return this.list.isVisible();
43470         },
43471         
43472         collapse : function()
43473         {
43474             if(!this.isExpanded()){
43475                 return;
43476             }
43477             this.list.hide();
43478             Roo.get(document).un('mousedown', this.collapseIf, this);
43479             Roo.get(document).un('mousewheel', this.collapseIf, this);
43480             this.fireEvent('collapse', this);
43481             this.validate();
43482         },
43483         
43484         expand : function()
43485         {
43486             Roo.log('expand');
43487
43488             if(this.isExpanded() || !this.hasFocus){
43489                 return;
43490             }
43491             
43492             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43493             this.list.setWidth(lw);
43494             
43495             this.list.show();
43496             this.restrictHeight();
43497             
43498             Roo.get(document).on('mousedown', this.collapseIf, this);
43499             Roo.get(document).on('mousewheel', this.collapseIf, this);
43500             
43501             this.fireEvent('expand', this);
43502         },
43503         
43504         restrictHeight : function()
43505         {
43506             this.list.alignTo(this.inputEl(), this.listAlign);
43507             this.list.alignTo(this.inputEl(), this.listAlign);
43508         },
43509         
43510         onViewOver : function(e, t)
43511         {
43512             if(this.inKeyMode){
43513                 return;
43514             }
43515             var item = this.view.findItemFromChild(t);
43516             
43517             if(item){
43518                 var index = this.view.indexOf(item);
43519                 this.select(index, false);
43520             }
43521         },
43522
43523         // private
43524         onViewClick : function(view, doFocus, el, e)
43525         {
43526             var index = this.view.getSelectedIndexes()[0];
43527             
43528             var r = this.store.getAt(index);
43529             
43530             if(r){
43531                 this.onSelect(r, index);
43532             }
43533             if(doFocus !== false && !this.blockFocus){
43534                 this.inputEl().focus();
43535             }
43536         },
43537         
43538         onViewMove : function(e, t)
43539         {
43540             this.inKeyMode = false;
43541         },
43542         
43543         select : function(index, scrollIntoView)
43544         {
43545             this.selectedIndex = index;
43546             this.view.select(index);
43547             if(scrollIntoView !== false){
43548                 var el = this.view.getNode(index);
43549                 if(el){
43550                     this.list.scrollChildIntoView(el, false);
43551                 }
43552             }
43553         },
43554         
43555         createList : function()
43556         {
43557             this.list = Roo.get(document.body).createChild({
43558                 tag: 'ul',
43559                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43560                 style: 'display:none'
43561             });
43562             
43563             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43564         },
43565         
43566         collapseIf : function(e)
43567         {
43568             var in_combo  = e.within(this.el);
43569             var in_list =  e.within(this.list);
43570             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43571             
43572             if (in_combo || in_list || is_list) {
43573                 return;
43574             }
43575             this.collapse();
43576         },
43577         
43578         onSelect : function(record, index)
43579         {
43580             if(this.fireEvent('beforeselect', this, record, index) !== false){
43581                 
43582                 this.setFlagClass(record.data.iso2);
43583                 this.setDialCode(record.data.dialCode);
43584                 this.hasFocus = false;
43585                 this.collapse();
43586                 this.fireEvent('select', this, record, index);
43587             }
43588         },
43589         
43590         flagEl : function()
43591         {
43592             var flag = this.el.select('div.flag',true).first();
43593             if(!flag){
43594                 return false;
43595             }
43596             return flag;
43597         },
43598         
43599         dialCodeHolderEl : function()
43600         {
43601             var d = this.el.select('input.dial-code-holder',true).first();
43602             if(!d){
43603                 return false;
43604             }
43605             return d;
43606         },
43607         
43608         setDialCode : function(v)
43609         {
43610             this.dialCodeHolder.dom.value = '+'+v;
43611         },
43612         
43613         setFlagClass : function(n)
43614         {
43615             this.flag.dom.className = 'flag '+n;
43616         },
43617         
43618         getValue : function()
43619         {
43620             var v = this.inputEl().getValue();
43621             if(this.dialCodeHolder) {
43622                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43623             }
43624             return v;
43625         },
43626         
43627         setValue : function(v)
43628         {
43629             var d = this.getDialCode(v);
43630             
43631             //invalid dial code
43632             if(v.length == 0 || !d || d.length == 0) {
43633                 if(this.rendered){
43634                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43635                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43636                 }
43637                 return;
43638             }
43639             
43640             //valid dial code
43641             this.setFlagClass(this.dialCodeMapping[d].iso2);
43642             this.setDialCode(d);
43643             this.inputEl().dom.value = v.replace('+'+d,'');
43644             this.hiddenEl().dom.value = this.getValue();
43645             
43646             this.validate();
43647         },
43648         
43649         getDialCode : function(v)
43650         {
43651             v = v ||  '';
43652             
43653             if (v.length == 0) {
43654                 return this.dialCodeHolder.dom.value;
43655             }
43656             
43657             var dialCode = "";
43658             if (v.charAt(0) != "+") {
43659                 return false;
43660             }
43661             var numericChars = "";
43662             for (var i = 1; i < v.length; i++) {
43663               var c = v.charAt(i);
43664               if (!isNaN(c)) {
43665                 numericChars += c;
43666                 if (this.dialCodeMapping[numericChars]) {
43667                   dialCode = v.substr(1, i);
43668                 }
43669                 if (numericChars.length == 4) {
43670                   break;
43671                 }
43672               }
43673             }
43674             return dialCode;
43675         },
43676         
43677         reset : function()
43678         {
43679             this.setValue(this.defaultDialCode);
43680             this.validate();
43681         },
43682         
43683         hiddenEl : function()
43684         {
43685             return this.el.select('input.hidden-tel-input',true).first();
43686         },
43687         
43688         // after setting val
43689         onKeyUp : function(e){
43690             this.setValue(this.getValue());
43691         },
43692         
43693         onKeyPress : function(e){
43694             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43695                 e.stopEvent();
43696             }
43697         }
43698         
43699 });
43700 /**
43701  * @class Roo.bootstrap.MoneyField
43702  * @extends Roo.bootstrap.ComboBox
43703  * Bootstrap MoneyField class
43704  * 
43705  * @constructor
43706  * Create a new MoneyField.
43707  * @param {Object} config Configuration options
43708  */
43709
43710 Roo.bootstrap.MoneyField = function(config) {
43711     
43712     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43713     
43714 };
43715
43716 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43717     
43718     /**
43719      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43720      */
43721     allowDecimals : true,
43722     /**
43723      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43724      */
43725     decimalSeparator : ".",
43726     /**
43727      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43728      */
43729     decimalPrecision : 0,
43730     /**
43731      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43732      */
43733     allowNegative : true,
43734     /**
43735      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43736      */
43737     allowZero: true,
43738     /**
43739      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43740      */
43741     minValue : Number.NEGATIVE_INFINITY,
43742     /**
43743      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43744      */
43745     maxValue : Number.MAX_VALUE,
43746     /**
43747      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43748      */
43749     minText : "The minimum value for this field is {0}",
43750     /**
43751      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43752      */
43753     maxText : "The maximum value for this field is {0}",
43754     /**
43755      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43756      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43757      */
43758     nanText : "{0} is not a valid number",
43759     /**
43760      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43761      */
43762     castInt : true,
43763     /**
43764      * @cfg {String} defaults currency of the MoneyField
43765      * value should be in lkey
43766      */
43767     defaultCurrency : false,
43768     /**
43769      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43770      */
43771     thousandsDelimiter : false,
43772     /**
43773      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43774      */
43775     max_length: false,
43776     
43777     inputlg : 9,
43778     inputmd : 9,
43779     inputsm : 9,
43780     inputxs : 6,
43781     
43782     store : false,
43783     
43784     getAutoCreate : function()
43785     {
43786         var align = this.labelAlign || this.parentLabelAlign();
43787         
43788         var id = Roo.id();
43789
43790         var cfg = {
43791             cls: 'form-group',
43792             cn: []
43793         };
43794
43795         var input =  {
43796             tag: 'input',
43797             id : id,
43798             cls : 'form-control roo-money-amount-input',
43799             autocomplete: 'new-password'
43800         };
43801         
43802         var hiddenInput = {
43803             tag: 'input',
43804             type: 'hidden',
43805             id: Roo.id(),
43806             cls: 'hidden-number-input'
43807         };
43808         
43809         if(this.max_length) {
43810             input.maxlength = this.max_length; 
43811         }
43812         
43813         if (this.name) {
43814             hiddenInput.name = this.name;
43815         }
43816
43817         if (this.disabled) {
43818             input.disabled = true;
43819         }
43820
43821         var clg = 12 - this.inputlg;
43822         var cmd = 12 - this.inputmd;
43823         var csm = 12 - this.inputsm;
43824         var cxs = 12 - this.inputxs;
43825         
43826         var container = {
43827             tag : 'div',
43828             cls : 'row roo-money-field',
43829             cn : [
43830                 {
43831                     tag : 'div',
43832                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43833                     cn : [
43834                         {
43835                             tag : 'div',
43836                             cls: 'roo-select2-container input-group',
43837                             cn: [
43838                                 {
43839                                     tag : 'input',
43840                                     cls : 'form-control roo-money-currency-input',
43841                                     autocomplete: 'new-password',
43842                                     readOnly : 1,
43843                                     name : this.currencyName
43844                                 },
43845                                 {
43846                                     tag :'span',
43847                                     cls : 'input-group-addon',
43848                                     cn : [
43849                                         {
43850                                             tag: 'span',
43851                                             cls: 'caret'
43852                                         }
43853                                     ]
43854                                 }
43855                             ]
43856                         }
43857                     ]
43858                 },
43859                 {
43860                     tag : 'div',
43861                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43862                     cn : [
43863                         {
43864                             tag: 'div',
43865                             cls: this.hasFeedback ? 'has-feedback' : '',
43866                             cn: [
43867                                 input
43868                             ]
43869                         }
43870                     ]
43871                 }
43872             ]
43873             
43874         };
43875         
43876         if (this.fieldLabel.length) {
43877             var indicator = {
43878                 tag: 'i',
43879                 tooltip: 'This field is required'
43880             };
43881
43882             var label = {
43883                 tag: 'label',
43884                 'for':  id,
43885                 cls: 'control-label',
43886                 cn: []
43887             };
43888
43889             var label_text = {
43890                 tag: 'span',
43891                 html: this.fieldLabel
43892             };
43893
43894             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43895             label.cn = [
43896                 indicator,
43897                 label_text
43898             ];
43899
43900             if(this.indicatorpos == 'right') {
43901                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43902                 label.cn = [
43903                     label_text,
43904                     indicator
43905                 ];
43906             }
43907
43908             if(align == 'left') {
43909                 container = {
43910                     tag: 'div',
43911                     cn: [
43912                         container
43913                     ]
43914                 };
43915
43916                 if(this.labelWidth > 12){
43917                     label.style = "width: " + this.labelWidth + 'px';
43918                 }
43919                 if(this.labelWidth < 13 && this.labelmd == 0){
43920                     this.labelmd = this.labelWidth;
43921                 }
43922                 if(this.labellg > 0){
43923                     label.cls += ' col-lg-' + this.labellg;
43924                     input.cls += ' col-lg-' + (12 - this.labellg);
43925                 }
43926                 if(this.labelmd > 0){
43927                     label.cls += ' col-md-' + this.labelmd;
43928                     container.cls += ' col-md-' + (12 - this.labelmd);
43929                 }
43930                 if(this.labelsm > 0){
43931                     label.cls += ' col-sm-' + this.labelsm;
43932                     container.cls += ' col-sm-' + (12 - this.labelsm);
43933                 }
43934                 if(this.labelxs > 0){
43935                     label.cls += ' col-xs-' + this.labelxs;
43936                     container.cls += ' col-xs-' + (12 - this.labelxs);
43937                 }
43938             }
43939         }
43940
43941         cfg.cn = [
43942             label,
43943             container,
43944             hiddenInput
43945         ];
43946         
43947         var settings = this;
43948
43949         ['xs','sm','md','lg'].map(function(size){
43950             if (settings[size]) {
43951                 cfg.cls += ' col-' + size + '-' + settings[size];
43952             }
43953         });
43954         
43955         return cfg;
43956     },
43957     
43958     initEvents : function()
43959     {
43960         this.indicator = this.indicatorEl();
43961         
43962         this.initCurrencyEvent();
43963         
43964         this.initNumberEvent();
43965     },
43966     
43967     initCurrencyEvent : function()
43968     {
43969         if (!this.store) {
43970             throw "can not find store for combo";
43971         }
43972         
43973         this.store = Roo.factory(this.store, Roo.data);
43974         this.store.parent = this;
43975         
43976         this.createList();
43977         
43978         this.triggerEl = this.el.select('.input-group-addon', true).first();
43979         
43980         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43981         
43982         var _this = this;
43983         
43984         (function(){
43985             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43986             _this.list.setWidth(lw);
43987         }).defer(100);
43988         
43989         this.list.on('mouseover', this.onViewOver, this);
43990         this.list.on('mousemove', this.onViewMove, this);
43991         this.list.on('scroll', this.onViewScroll, this);
43992         
43993         if(!this.tpl){
43994             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43995         }
43996         
43997         this.view = new Roo.View(this.list, this.tpl, {
43998             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43999         });
44000         
44001         this.view.on('click', this.onViewClick, this);
44002         
44003         this.store.on('beforeload', this.onBeforeLoad, this);
44004         this.store.on('load', this.onLoad, this);
44005         this.store.on('loadexception', this.onLoadException, this);
44006         
44007         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44008             "up" : function(e){
44009                 this.inKeyMode = true;
44010                 this.selectPrev();
44011             },
44012
44013             "down" : function(e){
44014                 if(!this.isExpanded()){
44015                     this.onTriggerClick();
44016                 }else{
44017                     this.inKeyMode = true;
44018                     this.selectNext();
44019                 }
44020             },
44021
44022             "enter" : function(e){
44023                 this.collapse();
44024                 
44025                 if(this.fireEvent("specialkey", this, e)){
44026                     this.onViewClick(false);
44027                 }
44028                 
44029                 return true;
44030             },
44031
44032             "esc" : function(e){
44033                 this.collapse();
44034             },
44035
44036             "tab" : function(e){
44037                 this.collapse();
44038                 
44039                 if(this.fireEvent("specialkey", this, e)){
44040                     this.onViewClick(false);
44041                 }
44042                 
44043                 return true;
44044             },
44045
44046             scope : this,
44047
44048             doRelay : function(foo, bar, hname){
44049                 if(hname == 'down' || this.scope.isExpanded()){
44050                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44051                 }
44052                 return true;
44053             },
44054
44055             forceKeyDown: true
44056         });
44057         
44058         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44059         
44060     },
44061     
44062     initNumberEvent : function(e)
44063     {
44064         this.inputEl().on("keydown" , this.fireKey,  this);
44065         this.inputEl().on("focus", this.onFocus,  this);
44066         this.inputEl().on("blur", this.onBlur,  this);
44067         
44068         this.inputEl().relayEvent('keyup', this);
44069         
44070         if(this.indicator){
44071             this.indicator.addClass('invisible');
44072         }
44073  
44074         this.originalValue = this.getValue();
44075         
44076         if(this.validationEvent == 'keyup'){
44077             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44078             this.inputEl().on('keyup', this.filterValidation, this);
44079         }
44080         else if(this.validationEvent !== false){
44081             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44082         }
44083         
44084         if(this.selectOnFocus){
44085             this.on("focus", this.preFocus, this);
44086             
44087         }
44088         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44089             this.inputEl().on("keypress", this.filterKeys, this);
44090         } else {
44091             this.inputEl().relayEvent('keypress', this);
44092         }
44093         
44094         var allowed = "0123456789";
44095         
44096         if(this.allowDecimals){
44097             allowed += this.decimalSeparator;
44098         }
44099         
44100         if(this.allowNegative){
44101             allowed += "-";
44102         }
44103         
44104         if(this.thousandsDelimiter) {
44105             allowed += ",";
44106         }
44107         
44108         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44109         
44110         var keyPress = function(e){
44111             
44112             var k = e.getKey();
44113             
44114             var c = e.getCharCode();
44115             
44116             if(
44117                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44118                     allowed.indexOf(String.fromCharCode(c)) === -1
44119             ){
44120                 e.stopEvent();
44121                 return;
44122             }
44123             
44124             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44125                 return;
44126             }
44127             
44128             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44129                 e.stopEvent();
44130             }
44131         };
44132         
44133         this.inputEl().on("keypress", keyPress, this);
44134         
44135     },
44136     
44137     onTriggerClick : function(e)
44138     {   
44139         if(this.disabled){
44140             return;
44141         }
44142         
44143         this.page = 0;
44144         this.loadNext = false;
44145         
44146         if(this.isExpanded()){
44147             this.collapse();
44148             return;
44149         }
44150         
44151         this.hasFocus = true;
44152         
44153         if(this.triggerAction == 'all') {
44154             this.doQuery(this.allQuery, true);
44155             return;
44156         }
44157         
44158         this.doQuery(this.getRawValue());
44159     },
44160     
44161     getCurrency : function()
44162     {   
44163         var v = this.currencyEl().getValue();
44164         
44165         return v;
44166     },
44167     
44168     restrictHeight : function()
44169     {
44170         this.list.alignTo(this.currencyEl(), this.listAlign);
44171         this.list.alignTo(this.currencyEl(), this.listAlign);
44172     },
44173     
44174     onViewClick : function(view, doFocus, el, e)
44175     {
44176         var index = this.view.getSelectedIndexes()[0];
44177         
44178         var r = this.store.getAt(index);
44179         
44180         if(r){
44181             this.onSelect(r, index);
44182         }
44183     },
44184     
44185     onSelect : function(record, index){
44186         
44187         if(this.fireEvent('beforeselect', this, record, index) !== false){
44188         
44189             this.setFromCurrencyData(index > -1 ? record.data : false);
44190             
44191             this.collapse();
44192             
44193             this.fireEvent('select', this, record, index);
44194         }
44195     },
44196     
44197     setFromCurrencyData : function(o)
44198     {
44199         var currency = '';
44200         
44201         this.lastCurrency = o;
44202         
44203         if (this.currencyField) {
44204             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44205         } else {
44206             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44207         }
44208         
44209         this.lastSelectionText = currency;
44210         
44211         //setting default currency
44212         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44213             this.setCurrency(this.defaultCurrency);
44214             return;
44215         }
44216         
44217         this.setCurrency(currency);
44218     },
44219     
44220     setFromData : function(o)
44221     {
44222         var c = {};
44223         
44224         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44225         
44226         this.setFromCurrencyData(c);
44227         
44228         var value = '';
44229         
44230         if (this.name) {
44231             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44232         } else {
44233             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44234         }
44235         
44236         this.setValue(value);
44237         
44238     },
44239     
44240     setCurrency : function(v)
44241     {   
44242         this.currencyValue = v;
44243         
44244         if(this.rendered){
44245             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44246             this.validate();
44247         }
44248     },
44249     
44250     setValue : function(v)
44251     {
44252         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44253         
44254         this.value = v;
44255         
44256         if(this.rendered){
44257             
44258             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44259             
44260             this.inputEl().dom.value = (v == '') ? '' :
44261                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44262             
44263             if(!this.allowZero && v === '0') {
44264                 this.hiddenEl().dom.value = '';
44265                 this.inputEl().dom.value = '';
44266             }
44267             
44268             this.validate();
44269         }
44270     },
44271     
44272     getRawValue : function()
44273     {
44274         var v = this.inputEl().getValue();
44275         
44276         return v;
44277     },
44278     
44279     getValue : function()
44280     {
44281         return this.fixPrecision(this.parseValue(this.getRawValue()));
44282     },
44283     
44284     parseValue : function(value)
44285     {
44286         if(this.thousandsDelimiter) {
44287             value += "";
44288             r = new RegExp(",", "g");
44289             value = value.replace(r, "");
44290         }
44291         
44292         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44293         return isNaN(value) ? '' : value;
44294         
44295     },
44296     
44297     fixPrecision : function(value)
44298     {
44299         if(this.thousandsDelimiter) {
44300             value += "";
44301             r = new RegExp(",", "g");
44302             value = value.replace(r, "");
44303         }
44304         
44305         var nan = isNaN(value);
44306         
44307         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44308             return nan ? '' : value;
44309         }
44310         return parseFloat(value).toFixed(this.decimalPrecision);
44311     },
44312     
44313     decimalPrecisionFcn : function(v)
44314     {
44315         return Math.floor(v);
44316     },
44317     
44318     validateValue : function(value)
44319     {
44320         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44321             return false;
44322         }
44323         
44324         var num = this.parseValue(value);
44325         
44326         if(isNaN(num)){
44327             this.markInvalid(String.format(this.nanText, value));
44328             return false;
44329         }
44330         
44331         if(num < this.minValue){
44332             this.markInvalid(String.format(this.minText, this.minValue));
44333             return false;
44334         }
44335         
44336         if(num > this.maxValue){
44337             this.markInvalid(String.format(this.maxText, this.maxValue));
44338             return false;
44339         }
44340         
44341         return true;
44342     },
44343     
44344     validate : function()
44345     {
44346         if(this.disabled || this.allowBlank){
44347             this.markValid();
44348             return true;
44349         }
44350         
44351         var currency = this.getCurrency();
44352         
44353         if(this.validateValue(this.getRawValue()) && currency.length){
44354             this.markValid();
44355             return true;
44356         }
44357         
44358         this.markInvalid();
44359         return false;
44360     },
44361     
44362     getName: function()
44363     {
44364         return this.name;
44365     },
44366     
44367     beforeBlur : function()
44368     {
44369         if(!this.castInt){
44370             return;
44371         }
44372         
44373         var v = this.parseValue(this.getRawValue());
44374         
44375         if(v || v == 0){
44376             this.setValue(v);
44377         }
44378     },
44379     
44380     onBlur : function()
44381     {
44382         this.beforeBlur();
44383         
44384         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44385             //this.el.removeClass(this.focusClass);
44386         }
44387         
44388         this.hasFocus = false;
44389         
44390         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44391             this.validate();
44392         }
44393         
44394         var v = this.getValue();
44395         
44396         if(String(v) !== String(this.startValue)){
44397             this.fireEvent('change', this, v, this.startValue);
44398         }
44399         
44400         this.fireEvent("blur", this);
44401     },
44402     
44403     inputEl : function()
44404     {
44405         return this.el.select('.roo-money-amount-input', true).first();
44406     },
44407     
44408     currencyEl : function()
44409     {
44410         return this.el.select('.roo-money-currency-input', true).first();
44411     },
44412     
44413     hiddenEl : function()
44414     {
44415         return this.el.select('input.hidden-number-input',true).first();
44416     }
44417     
44418 });/**
44419  * @class Roo.bootstrap.BezierSignature
44420  * @extends Roo.bootstrap.Component
44421  * Bootstrap BezierSignature class
44422  * This script refer to:
44423  *    Title: Signature Pad
44424  *    Author: szimek
44425  *    Availability: https://github.com/szimek/signature_pad
44426  *
44427  * @constructor
44428  * Create a new BezierSignature
44429  * @param {Object} config The config object
44430  */
44431
44432 Roo.bootstrap.BezierSignature = function(config){
44433     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44434     this.addEvents({
44435         "resize" : true
44436     });
44437 };
44438
44439 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44440 {
44441      
44442     curve_data: [],
44443     
44444     is_empty: true,
44445     
44446     mouse_btn_down: true,
44447     
44448     /**
44449      * @cfg {int} canvas height
44450      */
44451     canvas_height: '200px',
44452     
44453     /**
44454      * @cfg {float|function} Radius of a single dot.
44455      */ 
44456     dot_size: false,
44457     
44458     /**
44459      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44460      */
44461     min_width: 0.5,
44462     
44463     /**
44464      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44465      */
44466     max_width: 2.5,
44467     
44468     /**
44469      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44470      */
44471     throttle: 16,
44472     
44473     /**
44474      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44475      */
44476     min_distance: 5,
44477     
44478     /**
44479      * @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.
44480      */
44481     bg_color: 'rgba(0, 0, 0, 0)',
44482     
44483     /**
44484      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44485      */
44486     dot_color: 'black',
44487     
44488     /**
44489      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44490      */ 
44491     velocity_filter_weight: 0.7,
44492     
44493     /**
44494      * @cfg {function} Callback when stroke begin. 
44495      */
44496     onBegin: false,
44497     
44498     /**
44499      * @cfg {function} Callback when stroke end.
44500      */
44501     onEnd: false,
44502     
44503     getAutoCreate : function()
44504     {
44505         var cls = 'roo-signature column';
44506         
44507         if(this.cls){
44508             cls += ' ' + this.cls;
44509         }
44510         
44511         var col_sizes = [
44512             'lg',
44513             'md',
44514             'sm',
44515             'xs'
44516         ];
44517         
44518         for(var i = 0; i < col_sizes.length; i++) {
44519             if(this[col_sizes[i]]) {
44520                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44521             }
44522         }
44523         
44524         var cfg = {
44525             tag: 'div',
44526             cls: cls,
44527             cn: [
44528                 {
44529                     tag: 'div',
44530                     cls: 'roo-signature-body',
44531                     cn: [
44532                         {
44533                             tag: 'canvas',
44534                             cls: 'roo-signature-body-canvas',
44535                             height: this.canvas_height,
44536                             width: this.canvas_width
44537                         }
44538                     ]
44539                 },
44540                 {
44541                     tag: 'input',
44542                     type: 'file',
44543                     style: 'display: none'
44544                 }
44545             ]
44546         };
44547         
44548         return cfg;
44549     },
44550     
44551     initEvents: function() 
44552     {
44553         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44554         
44555         var canvas = this.canvasEl();
44556         
44557         // mouse && touch event swapping...
44558         canvas.dom.style.touchAction = 'none';
44559         canvas.dom.style.msTouchAction = 'none';
44560         
44561         this.mouse_btn_down = false;
44562         canvas.on('mousedown', this._handleMouseDown, this);
44563         canvas.on('mousemove', this._handleMouseMove, this);
44564         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44565         
44566         if (window.PointerEvent) {
44567             canvas.on('pointerdown', this._handleMouseDown, this);
44568             canvas.on('pointermove', this._handleMouseMove, this);
44569             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44570         }
44571         
44572         if ('ontouchstart' in window) {
44573             canvas.on('touchstart', this._handleTouchStart, this);
44574             canvas.on('touchmove', this._handleTouchMove, this);
44575             canvas.on('touchend', this._handleTouchEnd, this);
44576         }
44577         
44578         Roo.EventManager.onWindowResize(this.resize, this, true);
44579         
44580         // file input event
44581         this.fileEl().on('change', this.uploadImage, this);
44582         
44583         this.clear();
44584         
44585         this.resize();
44586     },
44587     
44588     resize: function(){
44589         
44590         var canvas = this.canvasEl().dom;
44591         var ctx = this.canvasElCtx();
44592         var img_data = false;
44593         
44594         if(canvas.width > 0) {
44595             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44596         }
44597         // setting canvas width will clean img data
44598         canvas.width = 0;
44599         
44600         var style = window.getComputedStyle ? 
44601             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44602             
44603         var padding_left = parseInt(style.paddingLeft) || 0;
44604         var padding_right = parseInt(style.paddingRight) || 0;
44605         
44606         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44607         
44608         if(img_data) {
44609             ctx.putImageData(img_data, 0, 0);
44610         }
44611     },
44612     
44613     _handleMouseDown: function(e)
44614     {
44615         if (e.browserEvent.which === 1) {
44616             this.mouse_btn_down = true;
44617             this.strokeBegin(e);
44618         }
44619     },
44620     
44621     _handleMouseMove: function (e)
44622     {
44623         if (this.mouse_btn_down) {
44624             this.strokeMoveUpdate(e);
44625         }
44626     },
44627     
44628     _handleMouseUp: function (e)
44629     {
44630         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44631             this.mouse_btn_down = false;
44632             this.strokeEnd(e);
44633         }
44634     },
44635     
44636     _handleTouchStart: function (e) {
44637         
44638         e.preventDefault();
44639         if (e.browserEvent.targetTouches.length === 1) {
44640             // var touch = e.browserEvent.changedTouches[0];
44641             // this.strokeBegin(touch);
44642             
44643              this.strokeBegin(e); // assume e catching the correct xy...
44644         }
44645     },
44646     
44647     _handleTouchMove: function (e) {
44648         e.preventDefault();
44649         // var touch = event.targetTouches[0];
44650         // _this._strokeMoveUpdate(touch);
44651         this.strokeMoveUpdate(e);
44652     },
44653     
44654     _handleTouchEnd: function (e) {
44655         var wasCanvasTouched = e.target === this.canvasEl().dom;
44656         if (wasCanvasTouched) {
44657             e.preventDefault();
44658             // var touch = event.changedTouches[0];
44659             // _this._strokeEnd(touch);
44660             this.strokeEnd(e);
44661         }
44662     },
44663     
44664     reset: function () {
44665         this._lastPoints = [];
44666         this._lastVelocity = 0;
44667         this._lastWidth = (this.min_width + this.max_width) / 2;
44668         this.canvasElCtx().fillStyle = this.dot_color;
44669     },
44670     
44671     strokeMoveUpdate: function(e)
44672     {
44673         this.strokeUpdate(e);
44674         
44675         if (this.throttle) {
44676             this.throttleStroke(this.strokeUpdate, this.throttle);
44677         }
44678         else {
44679             this.strokeUpdate(e);
44680         }
44681     },
44682     
44683     strokeBegin: function(e)
44684     {
44685         var newPointGroup = {
44686             color: this.dot_color,
44687             points: []
44688         };
44689         
44690         if (typeof this.onBegin === 'function') {
44691             this.onBegin(e);
44692         }
44693         
44694         this.curve_data.push(newPointGroup);
44695         this.reset();
44696         this.strokeUpdate(e);
44697     },
44698     
44699     strokeUpdate: function(e)
44700     {
44701         var rect = this.canvasEl().dom.getBoundingClientRect();
44702         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44703         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44704         var lastPoints = lastPointGroup.points;
44705         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44706         var isLastPointTooClose = lastPoint
44707             ? point.distanceTo(lastPoint) <= this.min_distance
44708             : false;
44709         var color = lastPointGroup.color;
44710         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44711             var curve = this.addPoint(point);
44712             if (!lastPoint) {
44713                 this.drawDot({color: color, point: point});
44714             }
44715             else if (curve) {
44716                 this.drawCurve({color: color, curve: curve});
44717             }
44718             lastPoints.push({
44719                 time: point.time,
44720                 x: point.x,
44721                 y: point.y
44722             });
44723         }
44724     },
44725     
44726     strokeEnd: function(e)
44727     {
44728         this.strokeUpdate(e);
44729         if (typeof this.onEnd === 'function') {
44730             this.onEnd(e);
44731         }
44732     },
44733     
44734     addPoint:  function (point) {
44735         var _lastPoints = this._lastPoints;
44736         _lastPoints.push(point);
44737         if (_lastPoints.length > 2) {
44738             if (_lastPoints.length === 3) {
44739                 _lastPoints.unshift(_lastPoints[0]);
44740             }
44741             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44742             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44743             _lastPoints.shift();
44744             return curve;
44745         }
44746         return null;
44747     },
44748     
44749     calculateCurveWidths: function (startPoint, endPoint) {
44750         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44751             (1 - this.velocity_filter_weight) * this._lastVelocity;
44752
44753         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44754         var widths = {
44755             end: newWidth,
44756             start: this._lastWidth
44757         };
44758         
44759         this._lastVelocity = velocity;
44760         this._lastWidth = newWidth;
44761         return widths;
44762     },
44763     
44764     drawDot: function (_a) {
44765         var color = _a.color, point = _a.point;
44766         var ctx = this.canvasElCtx();
44767         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44768         ctx.beginPath();
44769         this.drawCurveSegment(point.x, point.y, width);
44770         ctx.closePath();
44771         ctx.fillStyle = color;
44772         ctx.fill();
44773     },
44774     
44775     drawCurve: function (_a) {
44776         var color = _a.color, curve = _a.curve;
44777         var ctx = this.canvasElCtx();
44778         var widthDelta = curve.endWidth - curve.startWidth;
44779         var drawSteps = Math.floor(curve.length()) * 2;
44780         ctx.beginPath();
44781         ctx.fillStyle = color;
44782         for (var i = 0; i < drawSteps; i += 1) {
44783         var t = i / drawSteps;
44784         var tt = t * t;
44785         var ttt = tt * t;
44786         var u = 1 - t;
44787         var uu = u * u;
44788         var uuu = uu * u;
44789         var x = uuu * curve.startPoint.x;
44790         x += 3 * uu * t * curve.control1.x;
44791         x += 3 * u * tt * curve.control2.x;
44792         x += ttt * curve.endPoint.x;
44793         var y = uuu * curve.startPoint.y;
44794         y += 3 * uu * t * curve.control1.y;
44795         y += 3 * u * tt * curve.control2.y;
44796         y += ttt * curve.endPoint.y;
44797         var width = curve.startWidth + ttt * widthDelta;
44798         this.drawCurveSegment(x, y, width);
44799         }
44800         ctx.closePath();
44801         ctx.fill();
44802     },
44803     
44804     drawCurveSegment: function (x, y, width) {
44805         var ctx = this.canvasElCtx();
44806         ctx.moveTo(x, y);
44807         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44808         this.is_empty = false;
44809     },
44810     
44811     clear: function()
44812     {
44813         var ctx = this.canvasElCtx();
44814         var canvas = this.canvasEl().dom;
44815         ctx.fillStyle = this.bg_color;
44816         ctx.clearRect(0, 0, canvas.width, canvas.height);
44817         ctx.fillRect(0, 0, canvas.width, canvas.height);
44818         this.curve_data = [];
44819         this.reset();
44820         this.is_empty = true;
44821     },
44822     
44823     fileEl: function()
44824     {
44825         return  this.el.select('input',true).first();
44826     },
44827     
44828     canvasEl: function()
44829     {
44830         return this.el.select('canvas',true).first();
44831     },
44832     
44833     canvasElCtx: function()
44834     {
44835         return this.el.select('canvas',true).first().dom.getContext('2d');
44836     },
44837     
44838     getImage: function(type)
44839     {
44840         if(this.is_empty) {
44841             return false;
44842         }
44843         
44844         // encryption ?
44845         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44846     },
44847     
44848     drawFromImage: function(img_src)
44849     {
44850         var img = new Image();
44851         
44852         img.onload = function(){
44853             this.canvasElCtx().drawImage(img, 0, 0);
44854         }.bind(this);
44855         
44856         img.src = img_src;
44857         
44858         this.is_empty = false;
44859     },
44860     
44861     selectImage: function()
44862     {
44863         this.fileEl().dom.click();
44864     },
44865     
44866     uploadImage: function(e)
44867     {
44868         var reader = new FileReader();
44869         
44870         reader.onload = function(e){
44871             var img = new Image();
44872             img.onload = function(){
44873                 this.reset();
44874                 this.canvasElCtx().drawImage(img, 0, 0);
44875             }.bind(this);
44876             img.src = e.target.result;
44877         }.bind(this);
44878         
44879         reader.readAsDataURL(e.target.files[0]);
44880     },
44881     
44882     // Bezier Point Constructor
44883     Point: (function () {
44884         function Point(x, y, time) {
44885             this.x = x;
44886             this.y = y;
44887             this.time = time || Date.now();
44888         }
44889         Point.prototype.distanceTo = function (start) {
44890             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44891         };
44892         Point.prototype.equals = function (other) {
44893             return this.x === other.x && this.y === other.y && this.time === other.time;
44894         };
44895         Point.prototype.velocityFrom = function (start) {
44896             return this.time !== start.time
44897             ? this.distanceTo(start) / (this.time - start.time)
44898             : 0;
44899         };
44900         return Point;
44901     }()),
44902     
44903     
44904     // Bezier Constructor
44905     Bezier: (function () {
44906         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44907             this.startPoint = startPoint;
44908             this.control2 = control2;
44909             this.control1 = control1;
44910             this.endPoint = endPoint;
44911             this.startWidth = startWidth;
44912             this.endWidth = endWidth;
44913         }
44914         Bezier.fromPoints = function (points, widths, scope) {
44915             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44916             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44917             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44918         };
44919         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44920             var dx1 = s1.x - s2.x;
44921             var dy1 = s1.y - s2.y;
44922             var dx2 = s2.x - s3.x;
44923             var dy2 = s2.y - s3.y;
44924             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44925             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44926             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44927             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44928             var dxm = m1.x - m2.x;
44929             var dym = m1.y - m2.y;
44930             var k = l2 / (l1 + l2);
44931             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44932             var tx = s2.x - cm.x;
44933             var ty = s2.y - cm.y;
44934             return {
44935                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44936                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44937             };
44938         };
44939         Bezier.prototype.length = function () {
44940             var steps = 10;
44941             var length = 0;
44942             var px;
44943             var py;
44944             for (var i = 0; i <= steps; i += 1) {
44945                 var t = i / steps;
44946                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44947                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44948                 if (i > 0) {
44949                     var xdiff = cx - px;
44950                     var ydiff = cy - py;
44951                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44952                 }
44953                 px = cx;
44954                 py = cy;
44955             }
44956             return length;
44957         };
44958         Bezier.prototype.point = function (t, start, c1, c2, end) {
44959             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44960             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44961             + (3.0 * c2 * (1.0 - t) * t * t)
44962             + (end * t * t * t);
44963         };
44964         return Bezier;
44965     }()),
44966     
44967     throttleStroke: function(fn, wait) {
44968       if (wait === void 0) { wait = 250; }
44969       var previous = 0;
44970       var timeout = null;
44971       var result;
44972       var storedContext;
44973       var storedArgs;
44974       var later = function () {
44975           previous = Date.now();
44976           timeout = null;
44977           result = fn.apply(storedContext, storedArgs);
44978           if (!timeout) {
44979               storedContext = null;
44980               storedArgs = [];
44981           }
44982       };
44983       return function wrapper() {
44984           var args = [];
44985           for (var _i = 0; _i < arguments.length; _i++) {
44986               args[_i] = arguments[_i];
44987           }
44988           var now = Date.now();
44989           var remaining = wait - (now - previous);
44990           storedContext = this;
44991           storedArgs = args;
44992           if (remaining <= 0 || remaining > wait) {
44993               if (timeout) {
44994                   clearTimeout(timeout);
44995                   timeout = null;
44996               }
44997               previous = now;
44998               result = fn.apply(storedContext, storedArgs);
44999               if (!timeout) {
45000                   storedContext = null;
45001                   storedArgs = [];
45002               }
45003           }
45004           else if (!timeout) {
45005               timeout = window.setTimeout(later, remaining);
45006           }
45007           return result;
45008       };
45009   }
45010   
45011 });
45012
45013  
45014
45015