remove required for allowBlank
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     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  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         var c = config[i];
7316         if(typeof c.dataIndex == "undefined"){
7317             c.dataIndex = i;
7318         }
7319         if(typeof c.renderer == "string"){
7320             c.renderer = Roo.util.Format[c.renderer];
7321         }
7322         if(typeof c.id == "undefined"){
7323             c.id = Roo.id();
7324         }
7325         if(c.editor && c.editor.xtype){
7326             c.editor  = Roo.factory(c.editor, Roo.grid);
7327         }
7328         if(c.editor && c.editor.isFormField){
7329             c.editor = new Roo.grid.GridEditor(c.editor);
7330         }
7331         this.lookup[c.id] = c;
7332     }
7333
7334     /**
7335      * The width of columns which have no width specified (defaults to 100)
7336      * @type Number
7337      */
7338     this.defaultWidth = 100;
7339
7340     /**
7341      * Default sortable of columns which have no sortable specified (defaults to false)
7342      * @type Boolean
7343      */
7344     this.defaultSortable = false;
7345
7346     this.addEvents({
7347         /**
7348              * @event widthchange
7349              * Fires when the width of a column changes.
7350              * @param {ColumnModel} this
7351              * @param {Number} columnIndex The column index
7352              * @param {Number} newWidth The new width
7353              */
7354             "widthchange": true,
7355         /**
7356              * @event headerchange
7357              * Fires when the text of a header changes.
7358              * @param {ColumnModel} this
7359              * @param {Number} columnIndex The column index
7360              * @param {Number} newText The new header text
7361              */
7362             "headerchange": true,
7363         /**
7364              * @event hiddenchange
7365              * Fires when a column is hidden or "unhidden".
7366              * @param {ColumnModel} this
7367              * @param {Number} columnIndex The column index
7368              * @param {Boolean} hidden true if hidden, false otherwise
7369              */
7370             "hiddenchange": true,
7371             /**
7372          * @event columnmoved
7373          * Fires when a column is moved.
7374          * @param {ColumnModel} this
7375          * @param {Number} oldIndex
7376          * @param {Number} newIndex
7377          */
7378         "columnmoved" : true,
7379         /**
7380          * @event columlockchange
7381          * Fires when a column's locked state is changed
7382          * @param {ColumnModel} this
7383          * @param {Number} colIndex
7384          * @param {Boolean} locked true if locked
7385          */
7386         "columnlockchange" : true
7387     });
7388     Roo.grid.ColumnModel.superclass.constructor.call(this);
7389 };
7390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7391     /**
7392      * @cfg {String} header The header text to display in the Grid view.
7393      */
7394     /**
7395      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7396      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7397      * specified, the column's index is used as an index into the Record's data Array.
7398      */
7399     /**
7400      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7401      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7402      */
7403     /**
7404      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7405      * Defaults to the value of the {@link #defaultSortable} property.
7406      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7407      */
7408     /**
7409      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7410      */
7411     /**
7412      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7413      */
7414     /**
7415      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7416      */
7417     /**
7418      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7419      */
7420     /**
7421      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7422      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7423      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7424      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7425      */
7426        /**
7427      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7428      */
7429     /**
7430      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7431      */
7432     /**
7433      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7434      */
7435     /**
7436      * @cfg {String} cursor (Optional)
7437      */
7438     /**
7439      * @cfg {String} tooltip (Optional)
7440      */
7441     /**
7442      * @cfg {Number} xs (Optional)
7443      */
7444     /**
7445      * @cfg {Number} sm (Optional)
7446      */
7447     /**
7448      * @cfg {Number} md (Optional)
7449      */
7450     /**
7451      * @cfg {Number} lg (Optional)
7452      */
7453     /**
7454      * Returns the id of the column at the specified index.
7455      * @param {Number} index The column index
7456      * @return {String} the id
7457      */
7458     getColumnId : function(index){
7459         return this.config[index].id;
7460     },
7461
7462     /**
7463      * Returns the column for a specified id.
7464      * @param {String} id The column id
7465      * @return {Object} the column
7466      */
7467     getColumnById : function(id){
7468         return this.lookup[id];
7469     },
7470
7471     
7472     /**
7473      * Returns the column for a specified dataIndex.
7474      * @param {String} dataIndex The column dataIndex
7475      * @return {Object|Boolean} the column or false if not found
7476      */
7477     getColumnByDataIndex: function(dataIndex){
7478         var index = this.findColumnIndex(dataIndex);
7479         return index > -1 ? this.config[index] : false;
7480     },
7481     
7482     /**
7483      * Returns the index for a specified column id.
7484      * @param {String} id The column id
7485      * @return {Number} the index, or -1 if not found
7486      */
7487     getIndexById : function(id){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].id == id){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     /**
7497      * Returns the index for a specified column dataIndex.
7498      * @param {String} dataIndex The column dataIndex
7499      * @return {Number} the index, or -1 if not found
7500      */
7501     
7502     findColumnIndex : function(dataIndex){
7503         for(var i = 0, len = this.config.length; i < len; i++){
7504             if(this.config[i].dataIndex == dataIndex){
7505                 return i;
7506             }
7507         }
7508         return -1;
7509     },
7510     
7511     
7512     moveColumn : function(oldIndex, newIndex){
7513         var c = this.config[oldIndex];
7514         this.config.splice(oldIndex, 1);
7515         this.config.splice(newIndex, 0, c);
7516         this.dataMap = null;
7517         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7518     },
7519
7520     isLocked : function(colIndex){
7521         return this.config[colIndex].locked === true;
7522     },
7523
7524     setLocked : function(colIndex, value, suppressEvent){
7525         if(this.isLocked(colIndex) == value){
7526             return;
7527         }
7528         this.config[colIndex].locked = value;
7529         if(!suppressEvent){
7530             this.fireEvent("columnlockchange", this, colIndex, value);
7531         }
7532     },
7533
7534     getTotalLockedWidth : function(){
7535         var totalWidth = 0;
7536         for(var i = 0; i < this.config.length; i++){
7537             if(this.isLocked(i) && !this.isHidden(i)){
7538                 this.totalWidth += this.getColumnWidth(i);
7539             }
7540         }
7541         return totalWidth;
7542     },
7543
7544     getLockedCount : function(){
7545         for(var i = 0, len = this.config.length; i < len; i++){
7546             if(!this.isLocked(i)){
7547                 return i;
7548             }
7549         }
7550         
7551         return this.config.length;
7552     },
7553
7554     /**
7555      * Returns the number of columns.
7556      * @return {Number}
7557      */
7558     getColumnCount : function(visibleOnly){
7559         if(visibleOnly === true){
7560             var c = 0;
7561             for(var i = 0, len = this.config.length; i < len; i++){
7562                 if(!this.isHidden(i)){
7563                     c++;
7564                 }
7565             }
7566             return c;
7567         }
7568         return this.config.length;
7569     },
7570
7571     /**
7572      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7573      * @param {Function} fn
7574      * @param {Object} scope (optional)
7575      * @return {Array} result
7576      */
7577     getColumnsBy : function(fn, scope){
7578         var r = [];
7579         for(var i = 0, len = this.config.length; i < len; i++){
7580             var c = this.config[i];
7581             if(fn.call(scope||this, c, i) === true){
7582                 r[r.length] = c;
7583             }
7584         }
7585         return r;
7586     },
7587
7588     /**
7589      * Returns true if the specified column is sortable.
7590      * @param {Number} col The column index
7591      * @return {Boolean}
7592      */
7593     isSortable : function(col){
7594         if(typeof this.config[col].sortable == "undefined"){
7595             return this.defaultSortable;
7596         }
7597         return this.config[col].sortable;
7598     },
7599
7600     /**
7601      * Returns the rendering (formatting) function defined for the column.
7602      * @param {Number} col The column index.
7603      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7604      */
7605     getRenderer : function(col){
7606         if(!this.config[col].renderer){
7607             return Roo.grid.ColumnModel.defaultRenderer;
7608         }
7609         return this.config[col].renderer;
7610     },
7611
7612     /**
7613      * Sets the rendering (formatting) function for a column.
7614      * @param {Number} col The column index
7615      * @param {Function} fn The function to use to process the cell's raw data
7616      * to return HTML markup for the grid view. The render function is called with
7617      * the following parameters:<ul>
7618      * <li>Data value.</li>
7619      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7620      * <li>css A CSS style string to apply to the table cell.</li>
7621      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7622      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7623      * <li>Row index</li>
7624      * <li>Column index</li>
7625      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7626      */
7627     setRenderer : function(col, fn){
7628         this.config[col].renderer = fn;
7629     },
7630
7631     /**
7632      * Returns the width for the specified column.
7633      * @param {Number} col The column index
7634      * @return {Number}
7635      */
7636     getColumnWidth : function(col){
7637         return this.config[col].width * 1 || this.defaultWidth;
7638     },
7639
7640     /**
7641      * Sets the width for a column.
7642      * @param {Number} col The column index
7643      * @param {Number} width The new width
7644      */
7645     setColumnWidth : function(col, width, suppressEvent){
7646         this.config[col].width = width;
7647         this.totalWidth = null;
7648         if(!suppressEvent){
7649              this.fireEvent("widthchange", this, col, width);
7650         }
7651     },
7652
7653     /**
7654      * Returns the total width of all columns.
7655      * @param {Boolean} includeHidden True to include hidden column widths
7656      * @return {Number}
7657      */
7658     getTotalWidth : function(includeHidden){
7659         if(!this.totalWidth){
7660             this.totalWidth = 0;
7661             for(var i = 0, len = this.config.length; i < len; i++){
7662                 if(includeHidden || !this.isHidden(i)){
7663                     this.totalWidth += this.getColumnWidth(i);
7664                 }
7665             }
7666         }
7667         return this.totalWidth;
7668     },
7669
7670     /**
7671      * Returns the header for the specified column.
7672      * @param {Number} col The column index
7673      * @return {String}
7674      */
7675     getColumnHeader : function(col){
7676         return this.config[col].header;
7677     },
7678
7679     /**
7680      * Sets the header for a column.
7681      * @param {Number} col The column index
7682      * @param {String} header The new header
7683      */
7684     setColumnHeader : function(col, header){
7685         this.config[col].header = header;
7686         this.fireEvent("headerchange", this, col, header);
7687     },
7688
7689     /**
7690      * Returns the tooltip for the specified column.
7691      * @param {Number} col The column index
7692      * @return {String}
7693      */
7694     getColumnTooltip : function(col){
7695             return this.config[col].tooltip;
7696     },
7697     /**
7698      * Sets the tooltip for a column.
7699      * @param {Number} col The column index
7700      * @param {String} tooltip The new tooltip
7701      */
7702     setColumnTooltip : function(col, tooltip){
7703             this.config[col].tooltip = tooltip;
7704     },
7705
7706     /**
7707      * Returns the dataIndex for the specified column.
7708      * @param {Number} col The column index
7709      * @return {Number}
7710      */
7711     getDataIndex : function(col){
7712         return this.config[col].dataIndex;
7713     },
7714
7715     /**
7716      * Sets the dataIndex for a column.
7717      * @param {Number} col The column index
7718      * @param {Number} dataIndex The new dataIndex
7719      */
7720     setDataIndex : function(col, dataIndex){
7721         this.config[col].dataIndex = dataIndex;
7722     },
7723
7724     
7725     
7726     /**
7727      * Returns true if the cell is editable.
7728      * @param {Number} colIndex The column index
7729      * @param {Number} rowIndex The row index - this is nto actually used..?
7730      * @return {Boolean}
7731      */
7732     isCellEditable : function(colIndex, rowIndex){
7733         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7734     },
7735
7736     /**
7737      * Returns the editor defined for the cell/column.
7738      * return false or null to disable editing.
7739      * @param {Number} colIndex The column index
7740      * @param {Number} rowIndex The row index
7741      * @return {Object}
7742      */
7743     getCellEditor : function(colIndex, rowIndex){
7744         return this.config[colIndex].editor;
7745     },
7746
7747     /**
7748      * Sets if a column is editable.
7749      * @param {Number} col The column index
7750      * @param {Boolean} editable True if the column is editable
7751      */
7752     setEditable : function(col, editable){
7753         this.config[col].editable = editable;
7754     },
7755
7756
7757     /**
7758      * Returns true if the column is hidden.
7759      * @param {Number} colIndex The column index
7760      * @return {Boolean}
7761      */
7762     isHidden : function(colIndex){
7763         return this.config[colIndex].hidden;
7764     },
7765
7766
7767     /**
7768      * Returns true if the column width cannot be changed
7769      */
7770     isFixed : function(colIndex){
7771         return this.config[colIndex].fixed;
7772     },
7773
7774     /**
7775      * Returns true if the column can be resized
7776      * @return {Boolean}
7777      */
7778     isResizable : function(colIndex){
7779         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7780     },
7781     /**
7782      * Sets if a column is hidden.
7783      * @param {Number} colIndex The column index
7784      * @param {Boolean} hidden True if the column is hidden
7785      */
7786     setHidden : function(colIndex, hidden){
7787         this.config[colIndex].hidden = hidden;
7788         this.totalWidth = null;
7789         this.fireEvent("hiddenchange", this, colIndex, hidden);
7790     },
7791
7792     /**
7793      * Sets the editor for a column.
7794      * @param {Number} col The column index
7795      * @param {Object} editor The editor object
7796      */
7797     setEditor : function(col, editor){
7798         this.config[col].editor = editor;
7799     }
7800 });
7801
7802 Roo.grid.ColumnModel.defaultRenderer = function(value)
7803 {
7804     if(typeof value == "object") {
7805         return value;
7806     }
7807         if(typeof value == "string" && value.length < 1){
7808             return "&#160;";
7809         }
7810     
7811         return String.format("{0}", value);
7812 };
7813
7814 // Alias for backwards compatibility
7815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7816 /*
7817  * Based on:
7818  * Ext JS Library 1.1.1
7819  * Copyright(c) 2006-2007, Ext JS, LLC.
7820  *
7821  * Originally Released Under LGPL - original licence link has changed is not relivant.
7822  *
7823  * Fork - LGPL
7824  * <script type="text/javascript">
7825  */
7826  
7827 /**
7828  * @class Roo.LoadMask
7829  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7830  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7831  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7832  * element's UpdateManager load indicator and will be destroyed after the initial load.
7833  * @constructor
7834  * Create a new LoadMask
7835  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7836  * @param {Object} config The config object
7837  */
7838 Roo.LoadMask = function(el, config){
7839     this.el = Roo.get(el);
7840     Roo.apply(this, config);
7841     if(this.store){
7842         this.store.on('beforeload', this.onBeforeLoad, this);
7843         this.store.on('load', this.onLoad, this);
7844         this.store.on('loadexception', this.onLoadException, this);
7845         this.removeMask = false;
7846     }else{
7847         var um = this.el.getUpdateManager();
7848         um.showLoadIndicator = false; // disable the default indicator
7849         um.on('beforeupdate', this.onBeforeLoad, this);
7850         um.on('update', this.onLoad, this);
7851         um.on('failure', this.onLoad, this);
7852         this.removeMask = true;
7853     }
7854 };
7855
7856 Roo.LoadMask.prototype = {
7857     /**
7858      * @cfg {Boolean} removeMask
7859      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7860      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7861      */
7862     /**
7863      * @cfg {String} msg
7864      * The text to display in a centered loading message box (defaults to 'Loading...')
7865      */
7866     msg : 'Loading...',
7867     /**
7868      * @cfg {String} msgCls
7869      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7870      */
7871     msgCls : 'x-mask-loading',
7872
7873     /**
7874      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7875      * @type Boolean
7876      */
7877     disabled: false,
7878
7879     /**
7880      * Disables the mask to prevent it from being displayed
7881      */
7882     disable : function(){
7883        this.disabled = true;
7884     },
7885
7886     /**
7887      * Enables the mask so that it can be displayed
7888      */
7889     enable : function(){
7890         this.disabled = false;
7891     },
7892     
7893     onLoadException : function()
7894     {
7895         Roo.log(arguments);
7896         
7897         if (typeof(arguments[3]) != 'undefined') {
7898             Roo.MessageBox.alert("Error loading",arguments[3]);
7899         } 
7900         /*
7901         try {
7902             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7903                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7904             }   
7905         } catch(e) {
7906             
7907         }
7908         */
7909     
7910         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7911     },
7912     // private
7913     onLoad : function()
7914     {
7915         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7916     },
7917
7918     // private
7919     onBeforeLoad : function(){
7920         if(!this.disabled){
7921             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7922         }
7923     },
7924
7925     // private
7926     destroy : function(){
7927         if(this.store){
7928             this.store.un('beforeload', this.onBeforeLoad, this);
7929             this.store.un('load', this.onLoad, this);
7930             this.store.un('loadexception', this.onLoadException, this);
7931         }else{
7932             var um = this.el.getUpdateManager();
7933             um.un('beforeupdate', this.onBeforeLoad, this);
7934             um.un('update', this.onLoad, this);
7935             um.un('failure', this.onLoad, this);
7936         }
7937     }
7938 };/*
7939  * - LGPL
7940  *
7941  * table
7942  * 
7943  */
7944
7945 /**
7946  * @class Roo.bootstrap.Table
7947  * @extends Roo.bootstrap.Component
7948  * Bootstrap Table class
7949  * @cfg {String} cls table class
7950  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7951  * @cfg {String} bgcolor Specifies the background color for a table
7952  * @cfg {Number} border Specifies whether the table cells should have borders or not
7953  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7954  * @cfg {Number} cellspacing Specifies the space between cells
7955  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7956  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7957  * @cfg {String} sortable Specifies that the table should be sortable
7958  * @cfg {String} summary Specifies a summary of the content of a table
7959  * @cfg {Number} width Specifies the width of a table
7960  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7961  * 
7962  * @cfg {boolean} striped Should the rows be alternative striped
7963  * @cfg {boolean} bordered Add borders to the table
7964  * @cfg {boolean} hover Add hover highlighting
7965  * @cfg {boolean} condensed Format condensed
7966  * @cfg {boolean} responsive Format condensed
7967  * @cfg {Boolean} loadMask (true|false) default false
7968  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7969  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7970  * @cfg {Boolean} rowSelection (true|false) default false
7971  * @cfg {Boolean} cellSelection (true|false) default false
7972  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7973  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7974  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7975  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7976  
7977  * 
7978  * @constructor
7979  * Create a new Table
7980  * @param {Object} config The config object
7981  */
7982
7983 Roo.bootstrap.Table = function(config){
7984     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7985     
7986   
7987     
7988     // BC...
7989     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7990     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7991     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7992     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7993     
7994     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7995     if (this.sm) {
7996         this.sm.grid = this;
7997         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7998         this.sm = this.selModel;
7999         this.sm.xmodule = this.xmodule || false;
8000     }
8001     
8002     if (this.cm && typeof(this.cm.config) == 'undefined') {
8003         this.colModel = new Roo.grid.ColumnModel(this.cm);
8004         this.cm = this.colModel;
8005         this.cm.xmodule = this.xmodule || false;
8006     }
8007     if (this.store) {
8008         this.store= Roo.factory(this.store, Roo.data);
8009         this.ds = this.store;
8010         this.ds.xmodule = this.xmodule || false;
8011          
8012     }
8013     if (this.footer && this.store) {
8014         this.footer.dataSource = this.ds;
8015         this.footer = Roo.factory(this.footer);
8016     }
8017     
8018     /** @private */
8019     this.addEvents({
8020         /**
8021          * @event cellclick
8022          * Fires when a cell is clicked
8023          * @param {Roo.bootstrap.Table} this
8024          * @param {Roo.Element} el
8025          * @param {Number} rowIndex
8026          * @param {Number} columnIndex
8027          * @param {Roo.EventObject} e
8028          */
8029         "cellclick" : true,
8030         /**
8031          * @event celldblclick
8032          * Fires when a cell is double clicked
8033          * @param {Roo.bootstrap.Table} this
8034          * @param {Roo.Element} el
8035          * @param {Number} rowIndex
8036          * @param {Number} columnIndex
8037          * @param {Roo.EventObject} e
8038          */
8039         "celldblclick" : true,
8040         /**
8041          * @event rowclick
8042          * Fires when a row is clicked
8043          * @param {Roo.bootstrap.Table} this
8044          * @param {Roo.Element} el
8045          * @param {Number} rowIndex
8046          * @param {Roo.EventObject} e
8047          */
8048         "rowclick" : true,
8049         /**
8050          * @event rowdblclick
8051          * Fires when a row is double clicked
8052          * @param {Roo.bootstrap.Table} this
8053          * @param {Roo.Element} el
8054          * @param {Number} rowIndex
8055          * @param {Roo.EventObject} e
8056          */
8057         "rowdblclick" : true,
8058         /**
8059          * @event mouseover
8060          * Fires when a mouseover occur
8061          * @param {Roo.bootstrap.Table} this
8062          * @param {Roo.Element} el
8063          * @param {Number} rowIndex
8064          * @param {Number} columnIndex
8065          * @param {Roo.EventObject} e
8066          */
8067         "mouseover" : true,
8068         /**
8069          * @event mouseout
8070          * Fires when a mouseout occur
8071          * @param {Roo.bootstrap.Table} this
8072          * @param {Roo.Element} el
8073          * @param {Number} rowIndex
8074          * @param {Number} columnIndex
8075          * @param {Roo.EventObject} e
8076          */
8077         "mouseout" : true,
8078         /**
8079          * @event rowclass
8080          * Fires when a row is rendered, so you can change add a style to it.
8081          * @param {Roo.bootstrap.Table} this
8082          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8083          */
8084         'rowclass' : true,
8085           /**
8086          * @event rowsrendered
8087          * Fires when all the  rows have been rendered
8088          * @param {Roo.bootstrap.Table} this
8089          */
8090         'rowsrendered' : true,
8091         /**
8092          * @event contextmenu
8093          * The raw contextmenu event for the entire grid.
8094          * @param {Roo.EventObject} e
8095          */
8096         "contextmenu" : true,
8097         /**
8098          * @event rowcontextmenu
8099          * Fires when a row is right clicked
8100          * @param {Roo.bootstrap.Table} this
8101          * @param {Number} rowIndex
8102          * @param {Roo.EventObject} e
8103          */
8104         "rowcontextmenu" : true,
8105         /**
8106          * @event cellcontextmenu
8107          * Fires when a cell is right clicked
8108          * @param {Roo.bootstrap.Table} this
8109          * @param {Number} rowIndex
8110          * @param {Number} cellIndex
8111          * @param {Roo.EventObject} e
8112          */
8113          "cellcontextmenu" : true,
8114          /**
8115          * @event headercontextmenu
8116          * Fires when a header is right clicked
8117          * @param {Roo.bootstrap.Table} this
8118          * @param {Number} columnIndex
8119          * @param {Roo.EventObject} e
8120          */
8121         "headercontextmenu" : true
8122     });
8123 };
8124
8125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8126     
8127     cls: false,
8128     align: false,
8129     bgcolor: false,
8130     border: false,
8131     cellpadding: false,
8132     cellspacing: false,
8133     frame: false,
8134     rules: false,
8135     sortable: false,
8136     summary: false,
8137     width: false,
8138     striped : false,
8139     scrollBody : false,
8140     bordered: false,
8141     hover:  false,
8142     condensed : false,
8143     responsive : false,
8144     sm : false,
8145     cm : false,
8146     store : false,
8147     loadMask : false,
8148     footerShow : true,
8149     headerShow : true,
8150   
8151     rowSelection : false,
8152     cellSelection : false,
8153     layout : false,
8154     
8155     // Roo.Element - the tbody
8156     mainBody: false,
8157     // Roo.Element - thead element
8158     mainHead: false,
8159     
8160     container: false, // used by gridpanel...
8161     
8162     lazyLoad : false,
8163     
8164     CSS : Roo.util.CSS,
8165     
8166     auto_hide_footer : false,
8167     
8168     getAutoCreate : function()
8169     {
8170         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8171         
8172         cfg = {
8173             tag: 'table',
8174             cls : 'table',
8175             cn : []
8176         };
8177         if (this.scrollBody) {
8178             cfg.cls += ' table-body-fixed';
8179         }    
8180         if (this.striped) {
8181             cfg.cls += ' table-striped';
8182         }
8183         
8184         if (this.hover) {
8185             cfg.cls += ' table-hover';
8186         }
8187         if (this.bordered) {
8188             cfg.cls += ' table-bordered';
8189         }
8190         if (this.condensed) {
8191             cfg.cls += ' table-condensed';
8192         }
8193         if (this.responsive) {
8194             cfg.cls += ' table-responsive';
8195         }
8196         
8197         if (this.cls) {
8198             cfg.cls+=  ' ' +this.cls;
8199         }
8200         
8201         // this lot should be simplifed...
8202         var _t = this;
8203         var cp = [
8204             'align',
8205             'bgcolor',
8206             'border',
8207             'cellpadding',
8208             'cellspacing',
8209             'frame',
8210             'rules',
8211             'sortable',
8212             'summary',
8213             'width'
8214         ].forEach(function(k) {
8215             if (_t[k]) {
8216                 cfg[k] = _t[k];
8217             }
8218         });
8219         
8220         
8221         if (this.layout) {
8222             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8223         }
8224         
8225         if(this.store || this.cm){
8226             if(this.headerShow){
8227                 cfg.cn.push(this.renderHeader());
8228             }
8229             
8230             cfg.cn.push(this.renderBody());
8231             
8232             if(this.footerShow){
8233                 cfg.cn.push(this.renderFooter());
8234             }
8235             // where does this come from?
8236             //cfg.cls+=  ' TableGrid';
8237         }
8238         
8239         return { cn : [ cfg ] };
8240     },
8241     
8242     initEvents : function()
8243     {   
8244         if(!this.store || !this.cm){
8245             return;
8246         }
8247         if (this.selModel) {
8248             this.selModel.initEvents();
8249         }
8250         
8251         
8252         //Roo.log('initEvents with ds!!!!');
8253         
8254         this.mainBody = this.el.select('tbody', true).first();
8255         this.mainHead = this.el.select('thead', true).first();
8256         this.mainFoot = this.el.select('tfoot', true).first();
8257         
8258         
8259         
8260         var _this = this;
8261         
8262         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8263             e.on('click', _this.sort, _this);
8264         });
8265         
8266         this.mainBody.on("click", this.onClick, this);
8267         this.mainBody.on("dblclick", this.onDblClick, this);
8268         
8269         // why is this done????? = it breaks dialogs??
8270         //this.parent().el.setStyle('position', 'relative');
8271         
8272         
8273         if (this.footer) {
8274             this.footer.parentId = this.id;
8275             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8276             
8277             if(this.lazyLoad){
8278                 this.el.select('tfoot tr td').first().addClass('hide');
8279             }
8280         } 
8281         
8282         if(this.loadMask) {
8283             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8284         }
8285         
8286         this.store.on('load', this.onLoad, this);
8287         this.store.on('beforeload', this.onBeforeLoad, this);
8288         this.store.on('update', this.onUpdate, this);
8289         this.store.on('add', this.onAdd, this);
8290         this.store.on("clear", this.clear, this);
8291         
8292         this.el.on("contextmenu", this.onContextMenu, this);
8293         
8294         this.mainBody.on('scroll', this.onBodyScroll, this);
8295         
8296         this.cm.on("headerchange", this.onHeaderChange, this);
8297         
8298         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8299         
8300     },
8301     
8302     onContextMenu : function(e, t)
8303     {
8304         this.processEvent("contextmenu", e);
8305     },
8306     
8307     processEvent : function(name, e)
8308     {
8309         if (name != 'touchstart' ) {
8310             this.fireEvent(name, e);    
8311         }
8312         
8313         var t = e.getTarget();
8314         
8315         var cell = Roo.get(t);
8316         
8317         if(!cell){
8318             return;
8319         }
8320         
8321         if(cell.findParent('tfoot', false, true)){
8322             return;
8323         }
8324         
8325         if(cell.findParent('thead', false, true)){
8326             
8327             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8328                 cell = Roo.get(t).findParent('th', false, true);
8329                 if (!cell) {
8330                     Roo.log("failed to find th in thead?");
8331                     Roo.log(e.getTarget());
8332                     return;
8333                 }
8334             }
8335             
8336             var cellIndex = cell.dom.cellIndex;
8337             
8338             var ename = name == 'touchstart' ? 'click' : name;
8339             this.fireEvent("header" + ename, this, cellIndex, e);
8340             
8341             return;
8342         }
8343         
8344         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345             cell = Roo.get(t).findParent('td', false, true);
8346             if (!cell) {
8347                 Roo.log("failed to find th in tbody?");
8348                 Roo.log(e.getTarget());
8349                 return;
8350             }
8351         }
8352         
8353         var row = cell.findParent('tr', false, true);
8354         var cellIndex = cell.dom.cellIndex;
8355         var rowIndex = row.dom.rowIndex - 1;
8356         
8357         if(row !== false){
8358             
8359             this.fireEvent("row" + name, this, rowIndex, e);
8360             
8361             if(cell !== false){
8362             
8363                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8364             }
8365         }
8366         
8367     },
8368     
8369     onMouseover : function(e, el)
8370     {
8371         var cell = Roo.get(el);
8372         
8373         if(!cell){
8374             return;
8375         }
8376         
8377         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8378             cell = cell.findParent('td', false, true);
8379         }
8380         
8381         var row = cell.findParent('tr', false, true);
8382         var cellIndex = cell.dom.cellIndex;
8383         var rowIndex = row.dom.rowIndex - 1; // start from 0
8384         
8385         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8386         
8387     },
8388     
8389     onMouseout : function(e, el)
8390     {
8391         var cell = Roo.get(el);
8392         
8393         if(!cell){
8394             return;
8395         }
8396         
8397         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8398             cell = cell.findParent('td', false, true);
8399         }
8400         
8401         var row = cell.findParent('tr', false, true);
8402         var cellIndex = cell.dom.cellIndex;
8403         var rowIndex = row.dom.rowIndex - 1; // start from 0
8404         
8405         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8406         
8407     },
8408     
8409     onClick : function(e, el)
8410     {
8411         var cell = Roo.get(el);
8412         
8413         if(!cell || (!this.cellSelection && !this.rowSelection)){
8414             return;
8415         }
8416         
8417         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418             cell = cell.findParent('td', false, true);
8419         }
8420         
8421         if(!cell || typeof(cell) == 'undefined'){
8422             return;
8423         }
8424         
8425         var row = cell.findParent('tr', false, true);
8426         
8427         if(!row || typeof(row) == 'undefined'){
8428             return;
8429         }
8430         
8431         var cellIndex = cell.dom.cellIndex;
8432         var rowIndex = this.getRowIndex(row);
8433         
8434         // why??? - should these not be based on SelectionModel?
8435         if(this.cellSelection){
8436             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8437         }
8438         
8439         if(this.rowSelection){
8440             this.fireEvent('rowclick', this, row, rowIndex, e);
8441         }
8442         
8443         
8444     },
8445         
8446     onDblClick : function(e,el)
8447     {
8448         var cell = Roo.get(el);
8449         
8450         if(!cell || (!this.cellSelection && !this.rowSelection)){
8451             return;
8452         }
8453         
8454         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8455             cell = cell.findParent('td', false, true);
8456         }
8457         
8458         if(!cell || typeof(cell) == 'undefined'){
8459             return;
8460         }
8461         
8462         var row = cell.findParent('tr', false, true);
8463         
8464         if(!row || typeof(row) == 'undefined'){
8465             return;
8466         }
8467         
8468         var cellIndex = cell.dom.cellIndex;
8469         var rowIndex = this.getRowIndex(row);
8470         
8471         if(this.cellSelection){
8472             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8473         }
8474         
8475         if(this.rowSelection){
8476             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8477         }
8478     },
8479     
8480     sort : function(e,el)
8481     {
8482         var col = Roo.get(el);
8483         
8484         if(!col.hasClass('sortable')){
8485             return;
8486         }
8487         
8488         var sort = col.attr('sort');
8489         var dir = 'ASC';
8490         
8491         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8492             dir = 'DESC';
8493         }
8494         
8495         this.store.sortInfo = {field : sort, direction : dir};
8496         
8497         if (this.footer) {
8498             Roo.log("calling footer first");
8499             this.footer.onClick('first');
8500         } else {
8501         
8502             this.store.load({ params : { start : 0 } });
8503         }
8504     },
8505     
8506     renderHeader : function()
8507     {
8508         var header = {
8509             tag: 'thead',
8510             cn : []
8511         };
8512         
8513         var cm = this.cm;
8514         this.totalWidth = 0;
8515         
8516         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8517             
8518             var config = cm.config[i];
8519             
8520             var c = {
8521                 tag: 'th',
8522                 cls : 'x-hcol-' + i,
8523                 style : '',
8524                 html: cm.getColumnHeader(i)
8525             };
8526             
8527             var hh = '';
8528             
8529             if(typeof(config.sortable) != 'undefined' && config.sortable){
8530                 c.cls = 'sortable';
8531                 c.html = '<i class="glyphicon"></i>' + c.html;
8532             }
8533             
8534             // could use BS4 hidden-..-down 
8535             
8536             if(typeof(config.lgHeader) != 'undefined'){
8537                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8538             }
8539             
8540             if(typeof(config.mdHeader) != 'undefined'){
8541                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8542             }
8543             
8544             if(typeof(config.smHeader) != 'undefined'){
8545                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8546             }
8547             
8548             if(typeof(config.xsHeader) != 'undefined'){
8549                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8550             }
8551             
8552             if(hh.length){
8553                 c.html = hh;
8554             }
8555             
8556             if(typeof(config.tooltip) != 'undefined'){
8557                 c.tooltip = config.tooltip;
8558             }
8559             
8560             if(typeof(config.colspan) != 'undefined'){
8561                 c.colspan = config.colspan;
8562             }
8563             
8564             if(typeof(config.hidden) != 'undefined' && config.hidden){
8565                 c.style += ' display:none;';
8566             }
8567             
8568             if(typeof(config.dataIndex) != 'undefined'){
8569                 c.sort = config.dataIndex;
8570             }
8571             
8572            
8573             
8574             if(typeof(config.align) != 'undefined' && config.align.length){
8575                 c.style += ' text-align:' + config.align + ';';
8576             }
8577             
8578             if(typeof(config.width) != 'undefined'){
8579                 c.style += ' width:' + config.width + 'px;';
8580                 this.totalWidth += config.width;
8581             } else {
8582                 this.totalWidth += 100; // assume minimum of 100 per column?
8583             }
8584             
8585             if(typeof(config.cls) != 'undefined'){
8586                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8587             }
8588             
8589             ['xs','sm','md','lg'].map(function(size){
8590                 
8591                 if(typeof(config[size]) == 'undefined'){
8592                     return;
8593                 }
8594                  
8595                 if (!config[size]) { // 0 = hidden
8596                     // BS 4 '0' is treated as hide that column and below.
8597                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8598                     return;
8599                 }
8600                 
8601                 c.cls += ' col-' + size + '-' + config[size] + (
8602                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8603                 );
8604                 
8605                 
8606             });
8607             
8608             header.cn.push(c)
8609         }
8610         
8611         return header;
8612     },
8613     
8614     renderBody : function()
8615     {
8616         var body = {
8617             tag: 'tbody',
8618             cn : [
8619                 {
8620                     tag: 'tr',
8621                     cn : [
8622                         {
8623                             tag : 'td',
8624                             colspan :  this.cm.getColumnCount()
8625                         }
8626                     ]
8627                 }
8628             ]
8629         };
8630         
8631         return body;
8632     },
8633     
8634     renderFooter : function()
8635     {
8636         var footer = {
8637             tag: 'tfoot',
8638             cn : [
8639                 {
8640                     tag: 'tr',
8641                     cn : [
8642                         {
8643                             tag : 'td',
8644                             colspan :  this.cm.getColumnCount()
8645                         }
8646                     ]
8647                 }
8648             ]
8649         };
8650         
8651         return footer;
8652     },
8653     
8654     
8655     
8656     onLoad : function()
8657     {
8658 //        Roo.log('ds onload');
8659         this.clear();
8660         
8661         var _this = this;
8662         var cm = this.cm;
8663         var ds = this.store;
8664         
8665         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8666             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8667             if (_this.store.sortInfo) {
8668                     
8669                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8670                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8671                 }
8672                 
8673                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8674                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8675                 }
8676             }
8677         });
8678         
8679         var tbody =  this.mainBody;
8680               
8681         if(ds.getCount() > 0){
8682             ds.data.each(function(d,rowIndex){
8683                 var row =  this.renderRow(cm, ds, rowIndex);
8684                 
8685                 tbody.createChild(row);
8686                 
8687                 var _this = this;
8688                 
8689                 if(row.cellObjects.length){
8690                     Roo.each(row.cellObjects, function(r){
8691                         _this.renderCellObject(r);
8692                     })
8693                 }
8694                 
8695             }, this);
8696         }
8697         
8698         var tfoot = this.el.select('tfoot', true).first();
8699         
8700         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8701             
8702             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8703             
8704             var total = this.ds.getTotalCount();
8705             
8706             if(this.footer.pageSize < total){
8707                 this.mainFoot.show();
8708             }
8709         }
8710         
8711         Roo.each(this.el.select('tbody td', true).elements, function(e){
8712             e.on('mouseover', _this.onMouseover, _this);
8713         });
8714         
8715         Roo.each(this.el.select('tbody td', true).elements, function(e){
8716             e.on('mouseout', _this.onMouseout, _this);
8717         });
8718         this.fireEvent('rowsrendered', this);
8719         
8720         this.autoSize();
8721     },
8722     
8723     
8724     onUpdate : function(ds,record)
8725     {
8726         this.refreshRow(record);
8727         this.autoSize();
8728     },
8729     
8730     onRemove : function(ds, record, index, isUpdate){
8731         if(isUpdate !== true){
8732             this.fireEvent("beforerowremoved", this, index, record);
8733         }
8734         var bt = this.mainBody.dom;
8735         
8736         var rows = this.el.select('tbody > tr', true).elements;
8737         
8738         if(typeof(rows[index]) != 'undefined'){
8739             bt.removeChild(rows[index].dom);
8740         }
8741         
8742 //        if(bt.rows[index]){
8743 //            bt.removeChild(bt.rows[index]);
8744 //        }
8745         
8746         if(isUpdate !== true){
8747             //this.stripeRows(index);
8748             //this.syncRowHeights(index, index);
8749             //this.layout();
8750             this.fireEvent("rowremoved", this, index, record);
8751         }
8752     },
8753     
8754     onAdd : function(ds, records, rowIndex)
8755     {
8756         //Roo.log('on Add called');
8757         // - note this does not handle multiple adding very well..
8758         var bt = this.mainBody.dom;
8759         for (var i =0 ; i < records.length;i++) {
8760             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8761             //Roo.log(records[i]);
8762             //Roo.log(this.store.getAt(rowIndex+i));
8763             this.insertRow(this.store, rowIndex + i, false);
8764             return;
8765         }
8766         
8767     },
8768     
8769     
8770     refreshRow : function(record){
8771         var ds = this.store, index;
8772         if(typeof record == 'number'){
8773             index = record;
8774             record = ds.getAt(index);
8775         }else{
8776             index = ds.indexOf(record);
8777             if (index < 0) {
8778                 return; // should not happen - but seems to 
8779             }
8780         }
8781         this.insertRow(ds, index, true);
8782         this.autoSize();
8783         this.onRemove(ds, record, index+1, true);
8784         this.autoSize();
8785         //this.syncRowHeights(index, index);
8786         //this.layout();
8787         this.fireEvent("rowupdated", this, index, record);
8788     },
8789     
8790     insertRow : function(dm, rowIndex, isUpdate){
8791         
8792         if(!isUpdate){
8793             this.fireEvent("beforerowsinserted", this, rowIndex);
8794         }
8795             //var s = this.getScrollState();
8796         var row = this.renderRow(this.cm, this.store, rowIndex);
8797         // insert before rowIndex..
8798         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8799         
8800         var _this = this;
8801                 
8802         if(row.cellObjects.length){
8803             Roo.each(row.cellObjects, function(r){
8804                 _this.renderCellObject(r);
8805             })
8806         }
8807             
8808         if(!isUpdate){
8809             this.fireEvent("rowsinserted", this, rowIndex);
8810             //this.syncRowHeights(firstRow, lastRow);
8811             //this.stripeRows(firstRow);
8812             //this.layout();
8813         }
8814         
8815     },
8816     
8817     
8818     getRowDom : function(rowIndex)
8819     {
8820         var rows = this.el.select('tbody > tr', true).elements;
8821         
8822         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8823         
8824     },
8825     // returns the object tree for a tr..
8826   
8827     
8828     renderRow : function(cm, ds, rowIndex) 
8829     {
8830         var d = ds.getAt(rowIndex);
8831         
8832         var row = {
8833             tag : 'tr',
8834             cls : 'x-row-' + rowIndex,
8835             cn : []
8836         };
8837             
8838         var cellObjects = [];
8839         
8840         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8841             var config = cm.config[i];
8842             
8843             var renderer = cm.getRenderer(i);
8844             var value = '';
8845             var id = false;
8846             
8847             if(typeof(renderer) !== 'undefined'){
8848                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8849             }
8850             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8851             // and are rendered into the cells after the row is rendered - using the id for the element.
8852             
8853             if(typeof(value) === 'object'){
8854                 id = Roo.id();
8855                 cellObjects.push({
8856                     container : id,
8857                     cfg : value 
8858                 })
8859             }
8860             
8861             var rowcfg = {
8862                 record: d,
8863                 rowIndex : rowIndex,
8864                 colIndex : i,
8865                 rowClass : ''
8866             };
8867
8868             this.fireEvent('rowclass', this, rowcfg);
8869             
8870             var td = {
8871                 tag: 'td',
8872                 cls : rowcfg.rowClass + ' x-col-' + i,
8873                 style: '',
8874                 html: (typeof(value) === 'object') ? '' : value
8875             };
8876             
8877             if (id) {
8878                 td.id = id;
8879             }
8880             
8881             if(typeof(config.colspan) != 'undefined'){
8882                 td.colspan = config.colspan;
8883             }
8884             
8885             if(typeof(config.hidden) != 'undefined' && config.hidden){
8886                 td.style += ' display:none;';
8887             }
8888             
8889             if(typeof(config.align) != 'undefined' && config.align.length){
8890                 td.style += ' text-align:' + config.align + ';';
8891             }
8892             if(typeof(config.valign) != 'undefined' && config.valign.length){
8893                 td.style += ' vertical-align:' + config.valign + ';';
8894             }
8895             
8896             if(typeof(config.width) != 'undefined'){
8897                 td.style += ' width:' +  config.width + 'px;';
8898             }
8899             
8900             if(typeof(config.cursor) != 'undefined'){
8901                 td.style += ' cursor:' +  config.cursor + ';';
8902             }
8903             
8904             if(typeof(config.cls) != 'undefined'){
8905                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8906             }
8907             
8908             ['xs','sm','md','lg'].map(function(size){
8909                 
8910                 if(typeof(config[size]) == 'undefined'){
8911                     return;
8912                 }
8913                 
8914                 
8915                   
8916                 if (!config[size]) { // 0 = hidden
8917                     // BS 4 '0' is treated as hide that column and below.
8918                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8919                     return;
8920                 }
8921                 
8922                 td.cls += ' col-' + size + '-' + config[size] + (
8923                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8924                 );
8925                  
8926
8927             });
8928             
8929             row.cn.push(td);
8930            
8931         }
8932         
8933         row.cellObjects = cellObjects;
8934         
8935         return row;
8936           
8937     },
8938     
8939     
8940     
8941     onBeforeLoad : function()
8942     {
8943         
8944     },
8945      /**
8946      * Remove all rows
8947      */
8948     clear : function()
8949     {
8950         this.el.select('tbody', true).first().dom.innerHTML = '';
8951     },
8952     /**
8953      * Show or hide a row.
8954      * @param {Number} rowIndex to show or hide
8955      * @param {Boolean} state hide
8956      */
8957     setRowVisibility : function(rowIndex, state)
8958     {
8959         var bt = this.mainBody.dom;
8960         
8961         var rows = this.el.select('tbody > tr', true).elements;
8962         
8963         if(typeof(rows[rowIndex]) == 'undefined'){
8964             return;
8965         }
8966         rows[rowIndex].dom.style.display = state ? '' : 'none';
8967     },
8968     
8969     
8970     getSelectionModel : function(){
8971         if(!this.selModel){
8972             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8973         }
8974         return this.selModel;
8975     },
8976     /*
8977      * Render the Roo.bootstrap object from renderder
8978      */
8979     renderCellObject : function(r)
8980     {
8981         var _this = this;
8982         
8983         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8984         
8985         var t = r.cfg.render(r.container);
8986         
8987         if(r.cfg.cn){
8988             Roo.each(r.cfg.cn, function(c){
8989                 var child = {
8990                     container: t.getChildContainer(),
8991                     cfg: c
8992                 };
8993                 _this.renderCellObject(child);
8994             })
8995         }
8996     },
8997     
8998     getRowIndex : function(row)
8999     {
9000         var rowIndex = -1;
9001         
9002         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9003             if(el != row){
9004                 return;
9005             }
9006             
9007             rowIndex = index;
9008         });
9009         
9010         return rowIndex;
9011     },
9012      /**
9013      * Returns the grid's underlying element = used by panel.Grid
9014      * @return {Element} The element
9015      */
9016     getGridEl : function(){
9017         return this.el;
9018     },
9019      /**
9020      * Forces a resize - used by panel.Grid
9021      * @return {Element} The element
9022      */
9023     autoSize : function()
9024     {
9025         //var ctr = Roo.get(this.container.dom.parentElement);
9026         var ctr = Roo.get(this.el.dom);
9027         
9028         var thd = this.getGridEl().select('thead',true).first();
9029         var tbd = this.getGridEl().select('tbody', true).first();
9030         var tfd = this.getGridEl().select('tfoot', true).first();
9031         
9032         var cw = ctr.getWidth();
9033         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9034         
9035         if (tbd) {
9036             
9037             tbd.setWidth(ctr.getWidth());
9038             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9039             // this needs fixing for various usage - currently only hydra job advers I think..
9040             //tdb.setHeight(
9041             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9042             //); 
9043             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9044             cw -= barsize;
9045         }
9046         cw = Math.max(cw, this.totalWidth);
9047         this.getGridEl().select('tbody tr',true).setWidth(cw);
9048         
9049         // resize 'expandable coloumn?
9050         
9051         return; // we doe not have a view in this design..
9052         
9053     },
9054     onBodyScroll: function()
9055     {
9056         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9057         if(this.mainHead){
9058             this.mainHead.setStyle({
9059                 'position' : 'relative',
9060                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9061             });
9062         }
9063         
9064         if(this.lazyLoad){
9065             
9066             var scrollHeight = this.mainBody.dom.scrollHeight;
9067             
9068             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9069             
9070             var height = this.mainBody.getHeight();
9071             
9072             if(scrollHeight - height == scrollTop) {
9073                 
9074                 var total = this.ds.getTotalCount();
9075                 
9076                 if(this.footer.cursor + this.footer.pageSize < total){
9077                     
9078                     this.footer.ds.load({
9079                         params : {
9080                             start : this.footer.cursor + this.footer.pageSize,
9081                             limit : this.footer.pageSize
9082                         },
9083                         add : true
9084                     });
9085                 }
9086             }
9087             
9088         }
9089     },
9090     
9091     onHeaderChange : function()
9092     {
9093         var header = this.renderHeader();
9094         var table = this.el.select('table', true).first();
9095         
9096         this.mainHead.remove();
9097         this.mainHead = table.createChild(header, this.mainBody, false);
9098     },
9099     
9100     onHiddenChange : function(colModel, colIndex, hidden)
9101     {
9102         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9103         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9104         
9105         this.CSS.updateRule(thSelector, "display", "");
9106         this.CSS.updateRule(tdSelector, "display", "");
9107         
9108         if(hidden){
9109             this.CSS.updateRule(thSelector, "display", "none");
9110             this.CSS.updateRule(tdSelector, "display", "none");
9111         }
9112         
9113         this.onHeaderChange();
9114         this.onLoad();
9115     },
9116     
9117     setColumnWidth: function(col_index, width)
9118     {
9119         // width = "md-2 xs-2..."
9120         if(!this.colModel.config[col_index]) {
9121             return;
9122         }
9123         
9124         var w = width.split(" ");
9125         
9126         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9127         
9128         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9129         
9130         
9131         for(var j = 0; j < w.length; j++) {
9132             
9133             if(!w[j]) {
9134                 continue;
9135             }
9136             
9137             var size_cls = w[j].split("-");
9138             
9139             if(!Number.isInteger(size_cls[1] * 1)) {
9140                 continue;
9141             }
9142             
9143             if(!this.colModel.config[col_index][size_cls[0]]) {
9144                 continue;
9145             }
9146             
9147             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9148                 continue;
9149             }
9150             
9151             h_row[0].classList.replace(
9152                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9153                 "col-"+size_cls[0]+"-"+size_cls[1]
9154             );
9155             
9156             for(var i = 0; i < rows.length; i++) {
9157                 
9158                 var size_cls = w[j].split("-");
9159                 
9160                 if(!Number.isInteger(size_cls[1] * 1)) {
9161                     continue;
9162                 }
9163                 
9164                 if(!this.colModel.config[col_index][size_cls[0]]) {
9165                     continue;
9166                 }
9167                 
9168                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169                     continue;
9170                 }
9171                 
9172                 rows[i].classList.replace(
9173                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174                     "col-"+size_cls[0]+"-"+size_cls[1]
9175                 );
9176             }
9177             
9178             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9179         }
9180     }
9181 });
9182
9183  
9184
9185  /*
9186  * - LGPL
9187  *
9188  * table cell
9189  * 
9190  */
9191
9192 /**
9193  * @class Roo.bootstrap.TableCell
9194  * @extends Roo.bootstrap.Component
9195  * Bootstrap TableCell class
9196  * @cfg {String} html cell contain text
9197  * @cfg {String} cls cell class
9198  * @cfg {String} tag cell tag (td|th) default td
9199  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9200  * @cfg {String} align Aligns the content in a cell
9201  * @cfg {String} axis Categorizes cells
9202  * @cfg {String} bgcolor Specifies the background color of a cell
9203  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9204  * @cfg {Number} colspan Specifies the number of columns a cell should span
9205  * @cfg {String} headers Specifies one or more header cells a cell is related to
9206  * @cfg {Number} height Sets the height of a cell
9207  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9208  * @cfg {Number} rowspan Sets the number of rows a cell should span
9209  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9210  * @cfg {String} valign Vertical aligns the content in a cell
9211  * @cfg {Number} width Specifies the width of a cell
9212  * 
9213  * @constructor
9214  * Create a new TableCell
9215  * @param {Object} config The config object
9216  */
9217
9218 Roo.bootstrap.TableCell = function(config){
9219     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9220 };
9221
9222 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9223     
9224     html: false,
9225     cls: false,
9226     tag: false,
9227     abbr: false,
9228     align: false,
9229     axis: false,
9230     bgcolor: false,
9231     charoff: false,
9232     colspan: false,
9233     headers: false,
9234     height: false,
9235     nowrap: false,
9236     rowspan: false,
9237     scope: false,
9238     valign: false,
9239     width: false,
9240     
9241     
9242     getAutoCreate : function(){
9243         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9244         
9245         cfg = {
9246             tag: 'td'
9247         };
9248         
9249         if(this.tag){
9250             cfg.tag = this.tag;
9251         }
9252         
9253         if (this.html) {
9254             cfg.html=this.html
9255         }
9256         if (this.cls) {
9257             cfg.cls=this.cls
9258         }
9259         if (this.abbr) {
9260             cfg.abbr=this.abbr
9261         }
9262         if (this.align) {
9263             cfg.align=this.align
9264         }
9265         if (this.axis) {
9266             cfg.axis=this.axis
9267         }
9268         if (this.bgcolor) {
9269             cfg.bgcolor=this.bgcolor
9270         }
9271         if (this.charoff) {
9272             cfg.charoff=this.charoff
9273         }
9274         if (this.colspan) {
9275             cfg.colspan=this.colspan
9276         }
9277         if (this.headers) {
9278             cfg.headers=this.headers
9279         }
9280         if (this.height) {
9281             cfg.height=this.height
9282         }
9283         if (this.nowrap) {
9284             cfg.nowrap=this.nowrap
9285         }
9286         if (this.rowspan) {
9287             cfg.rowspan=this.rowspan
9288         }
9289         if (this.scope) {
9290             cfg.scope=this.scope
9291         }
9292         if (this.valign) {
9293             cfg.valign=this.valign
9294         }
9295         if (this.width) {
9296             cfg.width=this.width
9297         }
9298         
9299         
9300         return cfg;
9301     }
9302    
9303 });
9304
9305  
9306
9307  /*
9308  * - LGPL
9309  *
9310  * table row
9311  * 
9312  */
9313
9314 /**
9315  * @class Roo.bootstrap.TableRow
9316  * @extends Roo.bootstrap.Component
9317  * Bootstrap TableRow class
9318  * @cfg {String} cls row class
9319  * @cfg {String} align Aligns the content in a table row
9320  * @cfg {String} bgcolor Specifies a background color for a table row
9321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9322  * @cfg {String} valign Vertical aligns the content in a table row
9323  * 
9324  * @constructor
9325  * Create a new TableRow
9326  * @param {Object} config The config object
9327  */
9328
9329 Roo.bootstrap.TableRow = function(config){
9330     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9331 };
9332
9333 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9334     
9335     cls: false,
9336     align: false,
9337     bgcolor: false,
9338     charoff: false,
9339     valign: false,
9340     
9341     getAutoCreate : function(){
9342         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9343         
9344         cfg = {
9345             tag: 'tr'
9346         };
9347             
9348         if(this.cls){
9349             cfg.cls = this.cls;
9350         }
9351         if(this.align){
9352             cfg.align = this.align;
9353         }
9354         if(this.bgcolor){
9355             cfg.bgcolor = this.bgcolor;
9356         }
9357         if(this.charoff){
9358             cfg.charoff = this.charoff;
9359         }
9360         if(this.valign){
9361             cfg.valign = this.valign;
9362         }
9363         
9364         return cfg;
9365     }
9366    
9367 });
9368
9369  
9370
9371  /*
9372  * - LGPL
9373  *
9374  * table body
9375  * 
9376  */
9377
9378 /**
9379  * @class Roo.bootstrap.TableBody
9380  * @extends Roo.bootstrap.Component
9381  * Bootstrap TableBody class
9382  * @cfg {String} cls element class
9383  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9384  * @cfg {String} align Aligns the content inside the element
9385  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9386  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9387  * 
9388  * @constructor
9389  * Create a new TableBody
9390  * @param {Object} config The config object
9391  */
9392
9393 Roo.bootstrap.TableBody = function(config){
9394     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9398     
9399     cls: false,
9400     tag: false,
9401     align: false,
9402     charoff: false,
9403     valign: false,
9404     
9405     getAutoCreate : function(){
9406         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9407         
9408         cfg = {
9409             tag: 'tbody'
9410         };
9411             
9412         if (this.cls) {
9413             cfg.cls=this.cls
9414         }
9415         if(this.tag){
9416             cfg.tag = this.tag;
9417         }
9418         
9419         if(this.align){
9420             cfg.align = this.align;
9421         }
9422         if(this.charoff){
9423             cfg.charoff = this.charoff;
9424         }
9425         if(this.valign){
9426             cfg.valign = this.valign;
9427         }
9428         
9429         return cfg;
9430     }
9431     
9432     
9433 //    initEvents : function()
9434 //    {
9435 //        
9436 //        if(!this.store){
9437 //            return;
9438 //        }
9439 //        
9440 //        this.store = Roo.factory(this.store, Roo.data);
9441 //        this.store.on('load', this.onLoad, this);
9442 //        
9443 //        this.store.load();
9444 //        
9445 //    },
9446 //    
9447 //    onLoad: function () 
9448 //    {   
9449 //        this.fireEvent('load', this);
9450 //    }
9451 //    
9452 //   
9453 });
9454
9455  
9456
9457  /*
9458  * Based on:
9459  * Ext JS Library 1.1.1
9460  * Copyright(c) 2006-2007, Ext JS, LLC.
9461  *
9462  * Originally Released Under LGPL - original licence link has changed is not relivant.
9463  *
9464  * Fork - LGPL
9465  * <script type="text/javascript">
9466  */
9467
9468 // as we use this in bootstrap.
9469 Roo.namespace('Roo.form');
9470  /**
9471  * @class Roo.form.Action
9472  * Internal Class used to handle form actions
9473  * @constructor
9474  * @param {Roo.form.BasicForm} el The form element or its id
9475  * @param {Object} config Configuration options
9476  */
9477
9478  
9479  
9480 // define the action interface
9481 Roo.form.Action = function(form, options){
9482     this.form = form;
9483     this.options = options || {};
9484 };
9485 /**
9486  * Client Validation Failed
9487  * @const 
9488  */
9489 Roo.form.Action.CLIENT_INVALID = 'client';
9490 /**
9491  * Server Validation Failed
9492  * @const 
9493  */
9494 Roo.form.Action.SERVER_INVALID = 'server';
9495  /**
9496  * Connect to Server Failed
9497  * @const 
9498  */
9499 Roo.form.Action.CONNECT_FAILURE = 'connect';
9500 /**
9501  * Reading Data from Server Failed
9502  * @const 
9503  */
9504 Roo.form.Action.LOAD_FAILURE = 'load';
9505
9506 Roo.form.Action.prototype = {
9507     type : 'default',
9508     failureType : undefined,
9509     response : undefined,
9510     result : undefined,
9511
9512     // interface method
9513     run : function(options){
9514
9515     },
9516
9517     // interface method
9518     success : function(response){
9519
9520     },
9521
9522     // interface method
9523     handleResponse : function(response){
9524
9525     },
9526
9527     // default connection failure
9528     failure : function(response){
9529         
9530         this.response = response;
9531         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9532         this.form.afterAction(this, false);
9533     },
9534
9535     processResponse : function(response){
9536         this.response = response;
9537         if(!response.responseText){
9538             return true;
9539         }
9540         this.result = this.handleResponse(response);
9541         return this.result;
9542     },
9543
9544     // utility functions used internally
9545     getUrl : function(appendParams){
9546         var url = this.options.url || this.form.url || this.form.el.dom.action;
9547         if(appendParams){
9548             var p = this.getParams();
9549             if(p){
9550                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9551             }
9552         }
9553         return url;
9554     },
9555
9556     getMethod : function(){
9557         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9558     },
9559
9560     getParams : function(){
9561         var bp = this.form.baseParams;
9562         var p = this.options.params;
9563         if(p){
9564             if(typeof p == "object"){
9565                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9566             }else if(typeof p == 'string' && bp){
9567                 p += '&' + Roo.urlEncode(bp);
9568             }
9569         }else if(bp){
9570             p = Roo.urlEncode(bp);
9571         }
9572         return p;
9573     },
9574
9575     createCallback : function(){
9576         return {
9577             success: this.success,
9578             failure: this.failure,
9579             scope: this,
9580             timeout: (this.form.timeout*1000),
9581             upload: this.form.fileUpload ? this.success : undefined
9582         };
9583     }
9584 };
9585
9586 Roo.form.Action.Submit = function(form, options){
9587     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9588 };
9589
9590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9591     type : 'submit',
9592
9593     haveProgress : false,
9594     uploadComplete : false,
9595     
9596     // uploadProgress indicator.
9597     uploadProgress : function()
9598     {
9599         if (!this.form.progressUrl) {
9600             return;
9601         }
9602         
9603         if (!this.haveProgress) {
9604             Roo.MessageBox.progress("Uploading", "Uploading");
9605         }
9606         if (this.uploadComplete) {
9607            Roo.MessageBox.hide();
9608            return;
9609         }
9610         
9611         this.haveProgress = true;
9612    
9613         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9614         
9615         var c = new Roo.data.Connection();
9616         c.request({
9617             url : this.form.progressUrl,
9618             params: {
9619                 id : uid
9620             },
9621             method: 'GET',
9622             success : function(req){
9623                //console.log(data);
9624                 var rdata = false;
9625                 var edata;
9626                 try  {
9627                    rdata = Roo.decode(req.responseText)
9628                 } catch (e) {
9629                     Roo.log("Invalid data from server..");
9630                     Roo.log(edata);
9631                     return;
9632                 }
9633                 if (!rdata || !rdata.success) {
9634                     Roo.log(rdata);
9635                     Roo.MessageBox.alert(Roo.encode(rdata));
9636                     return;
9637                 }
9638                 var data = rdata.data;
9639                 
9640                 if (this.uploadComplete) {
9641                    Roo.MessageBox.hide();
9642                    return;
9643                 }
9644                    
9645                 if (data){
9646                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9647                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9648                     );
9649                 }
9650                 this.uploadProgress.defer(2000,this);
9651             },
9652        
9653             failure: function(data) {
9654                 Roo.log('progress url failed ');
9655                 Roo.log(data);
9656             },
9657             scope : this
9658         });
9659            
9660     },
9661     
9662     
9663     run : function()
9664     {
9665         // run get Values on the form, so it syncs any secondary forms.
9666         this.form.getValues();
9667         
9668         var o = this.options;
9669         var method = this.getMethod();
9670         var isPost = method == 'POST';
9671         if(o.clientValidation === false || this.form.isValid()){
9672             
9673             if (this.form.progressUrl) {
9674                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9675                     (new Date() * 1) + '' + Math.random());
9676                     
9677             } 
9678             
9679             
9680             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9681                 form:this.form.el.dom,
9682                 url:this.getUrl(!isPost),
9683                 method: method,
9684                 params:isPost ? this.getParams() : null,
9685                 isUpload: this.form.fileUpload,
9686                 formData : this.form.formData
9687             }));
9688             
9689             this.uploadProgress();
9690
9691         }else if (o.clientValidation !== false){ // client validation failed
9692             this.failureType = Roo.form.Action.CLIENT_INVALID;
9693             this.form.afterAction(this, false);
9694         }
9695     },
9696
9697     success : function(response)
9698     {
9699         this.uploadComplete= true;
9700         if (this.haveProgress) {
9701             Roo.MessageBox.hide();
9702         }
9703         
9704         
9705         var result = this.processResponse(response);
9706         if(result === true || result.success){
9707             this.form.afterAction(this, true);
9708             return;
9709         }
9710         if(result.errors){
9711             this.form.markInvalid(result.errors);
9712             this.failureType = Roo.form.Action.SERVER_INVALID;
9713         }
9714         this.form.afterAction(this, false);
9715     },
9716     failure : function(response)
9717     {
9718         this.uploadComplete= true;
9719         if (this.haveProgress) {
9720             Roo.MessageBox.hide();
9721         }
9722         
9723         this.response = response;
9724         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9725         this.form.afterAction(this, false);
9726     },
9727     
9728     handleResponse : function(response){
9729         if(this.form.errorReader){
9730             var rs = this.form.errorReader.read(response);
9731             var errors = [];
9732             if(rs.records){
9733                 for(var i = 0, len = rs.records.length; i < len; i++) {
9734                     var r = rs.records[i];
9735                     errors[i] = r.data;
9736                 }
9737             }
9738             if(errors.length < 1){
9739                 errors = null;
9740             }
9741             return {
9742                 success : rs.success,
9743                 errors : errors
9744             };
9745         }
9746         var ret = false;
9747         try {
9748             ret = Roo.decode(response.responseText);
9749         } catch (e) {
9750             ret = {
9751                 success: false,
9752                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9753                 errors : []
9754             };
9755         }
9756         return ret;
9757         
9758     }
9759 });
9760
9761
9762 Roo.form.Action.Load = function(form, options){
9763     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9764     this.reader = this.form.reader;
9765 };
9766
9767 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9768     type : 'load',
9769
9770     run : function(){
9771         
9772         Roo.Ajax.request(Roo.apply(
9773                 this.createCallback(), {
9774                     method:this.getMethod(),
9775                     url:this.getUrl(false),
9776                     params:this.getParams()
9777         }));
9778     },
9779
9780     success : function(response){
9781         
9782         var result = this.processResponse(response);
9783         if(result === true || !result.success || !result.data){
9784             this.failureType = Roo.form.Action.LOAD_FAILURE;
9785             this.form.afterAction(this, false);
9786             return;
9787         }
9788         this.form.clearInvalid();
9789         this.form.setValues(result.data);
9790         this.form.afterAction(this, true);
9791     },
9792
9793     handleResponse : function(response){
9794         if(this.form.reader){
9795             var rs = this.form.reader.read(response);
9796             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9797             return {
9798                 success : rs.success,
9799                 data : data
9800             };
9801         }
9802         return Roo.decode(response.responseText);
9803     }
9804 });
9805
9806 Roo.form.Action.ACTION_TYPES = {
9807     'load' : Roo.form.Action.Load,
9808     'submit' : Roo.form.Action.Submit
9809 };/*
9810  * - LGPL
9811  *
9812  * form
9813  *
9814  */
9815
9816 /**
9817  * @class Roo.bootstrap.Form
9818  * @extends Roo.bootstrap.Component
9819  * Bootstrap Form class
9820  * @cfg {String} method  GET | POST (default POST)
9821  * @cfg {String} labelAlign top | left (default top)
9822  * @cfg {String} align left  | right - for navbars
9823  * @cfg {Boolean} loadMask load mask when submit (default true)
9824
9825  *
9826  * @constructor
9827  * Create a new Form
9828  * @param {Object} config The config object
9829  */
9830
9831
9832 Roo.bootstrap.Form = function(config){
9833     
9834     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9835     
9836     Roo.bootstrap.Form.popover.apply();
9837     
9838     this.addEvents({
9839         /**
9840          * @event clientvalidation
9841          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9842          * @param {Form} this
9843          * @param {Boolean} valid true if the form has passed client-side validation
9844          */
9845         clientvalidation: true,
9846         /**
9847          * @event beforeaction
9848          * Fires before any action is performed. Return false to cancel the action.
9849          * @param {Form} this
9850          * @param {Action} action The action to be performed
9851          */
9852         beforeaction: true,
9853         /**
9854          * @event actionfailed
9855          * Fires when an action fails.
9856          * @param {Form} this
9857          * @param {Action} action The action that failed
9858          */
9859         actionfailed : true,
9860         /**
9861          * @event actioncomplete
9862          * Fires when an action is completed.
9863          * @param {Form} this
9864          * @param {Action} action The action that completed
9865          */
9866         actioncomplete : true
9867     });
9868 };
9869
9870 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9871
9872      /**
9873      * @cfg {String} method
9874      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9875      */
9876     method : 'POST',
9877     /**
9878      * @cfg {String} url
9879      * The URL to use for form actions if one isn't supplied in the action options.
9880      */
9881     /**
9882      * @cfg {Boolean} fileUpload
9883      * Set to true if this form is a file upload.
9884      */
9885
9886     /**
9887      * @cfg {Object} baseParams
9888      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9889      */
9890
9891     /**
9892      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9893      */
9894     timeout: 30,
9895     /**
9896      * @cfg {Sting} align (left|right) for navbar forms
9897      */
9898     align : 'left',
9899
9900     // private
9901     activeAction : null,
9902
9903     /**
9904      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9905      * element by passing it or its id or mask the form itself by passing in true.
9906      * @type Mixed
9907      */
9908     waitMsgTarget : false,
9909
9910     loadMask : true,
9911     
9912     /**
9913      * @cfg {Boolean} errorMask (true|false) default false
9914      */
9915     errorMask : false,
9916     
9917     /**
9918      * @cfg {Number} maskOffset Default 100
9919      */
9920     maskOffset : 100,
9921     
9922     /**
9923      * @cfg {Boolean} maskBody
9924      */
9925     maskBody : false,
9926
9927     getAutoCreate : function(){
9928
9929         var cfg = {
9930             tag: 'form',
9931             method : this.method || 'POST',
9932             id : this.id || Roo.id(),
9933             cls : ''
9934         };
9935         if (this.parent().xtype.match(/^Nav/)) {
9936             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9937
9938         }
9939
9940         if (this.labelAlign == 'left' ) {
9941             cfg.cls += ' form-horizontal';
9942         }
9943
9944
9945         return cfg;
9946     },
9947     initEvents : function()
9948     {
9949         this.el.on('submit', this.onSubmit, this);
9950         // this was added as random key presses on the form where triggering form submit.
9951         this.el.on('keypress', function(e) {
9952             if (e.getCharCode() != 13) {
9953                 return true;
9954             }
9955             // we might need to allow it for textareas.. and some other items.
9956             // check e.getTarget().
9957
9958             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9959                 return true;
9960             }
9961
9962             Roo.log("keypress blocked");
9963
9964             e.preventDefault();
9965             return false;
9966         });
9967         
9968     },
9969     // private
9970     onSubmit : function(e){
9971         e.stopEvent();
9972     },
9973
9974      /**
9975      * Returns true if client-side validation on the form is successful.
9976      * @return Boolean
9977      */
9978     isValid : function(){
9979         var items = this.getItems();
9980         var valid = true;
9981         var target = false;
9982         
9983         items.each(function(f){
9984             
9985             if(f.validate()){
9986                 return;
9987             }
9988             
9989             Roo.log('invalid field: ' + f.name);
9990             
9991             valid = false;
9992
9993             if(!target && f.el.isVisible(true)){
9994                 target = f;
9995             }
9996            
9997         });
9998         
9999         if(this.errorMask && !valid){
10000             Roo.bootstrap.Form.popover.mask(this, target);
10001         }
10002         
10003         return valid;
10004     },
10005     
10006     /**
10007      * Returns true if any fields in this form have changed since their original load.
10008      * @return Boolean
10009      */
10010     isDirty : function(){
10011         var dirty = false;
10012         var items = this.getItems();
10013         items.each(function(f){
10014            if(f.isDirty()){
10015                dirty = true;
10016                return false;
10017            }
10018            return true;
10019         });
10020         return dirty;
10021     },
10022      /**
10023      * Performs a predefined action (submit or load) or custom actions you define on this form.
10024      * @param {String} actionName The name of the action type
10025      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10026      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10027      * accept other config options):
10028      * <pre>
10029 Property          Type             Description
10030 ----------------  ---------------  ----------------------------------------------------------------------------------
10031 url               String           The url for the action (defaults to the form's url)
10032 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10033 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10034 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10035                                    validate the form on the client (defaults to false)
10036      * </pre>
10037      * @return {BasicForm} this
10038      */
10039     doAction : function(action, options){
10040         if(typeof action == 'string'){
10041             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10042         }
10043         if(this.fireEvent('beforeaction', this, action) !== false){
10044             this.beforeAction(action);
10045             action.run.defer(100, action);
10046         }
10047         return this;
10048     },
10049
10050     // private
10051     beforeAction : function(action){
10052         var o = action.options;
10053         
10054         if(this.loadMask){
10055             
10056             if(this.maskBody){
10057                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10058             } else {
10059                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10060             }
10061         }
10062         // not really supported yet.. ??
10063
10064         //if(this.waitMsgTarget === true){
10065         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066         //}else if(this.waitMsgTarget){
10067         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10068         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10069         //}else {
10070         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10071        // }
10072
10073     },
10074
10075     // private
10076     afterAction : function(action, success){
10077         this.activeAction = null;
10078         var o = action.options;
10079
10080         if(this.loadMask){
10081             
10082             if(this.maskBody){
10083                 Roo.get(document.body).unmask();
10084             } else {
10085                 this.el.unmask();
10086             }
10087         }
10088         
10089         //if(this.waitMsgTarget === true){
10090 //            this.el.unmask();
10091         //}else if(this.waitMsgTarget){
10092         //    this.waitMsgTarget.unmask();
10093         //}else{
10094         //    Roo.MessageBox.updateProgress(1);
10095         //    Roo.MessageBox.hide();
10096        // }
10097         //
10098         if(success){
10099             if(o.reset){
10100                 this.reset();
10101             }
10102             Roo.callback(o.success, o.scope, [this, action]);
10103             this.fireEvent('actioncomplete', this, action);
10104
10105         }else{
10106
10107             // failure condition..
10108             // we have a scenario where updates need confirming.
10109             // eg. if a locking scenario exists..
10110             // we look for { errors : { needs_confirm : true }} in the response.
10111             if (
10112                 (typeof(action.result) != 'undefined')  &&
10113                 (typeof(action.result.errors) != 'undefined')  &&
10114                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10115            ){
10116                 var _t = this;
10117                 Roo.log("not supported yet");
10118                  /*
10119
10120                 Roo.MessageBox.confirm(
10121                     "Change requires confirmation",
10122                     action.result.errorMsg,
10123                     function(r) {
10124                         if (r != 'yes') {
10125                             return;
10126                         }
10127                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10128                     }
10129
10130                 );
10131                 */
10132
10133
10134                 return;
10135             }
10136
10137             Roo.callback(o.failure, o.scope, [this, action]);
10138             // show an error message if no failed handler is set..
10139             if (!this.hasListener('actionfailed')) {
10140                 Roo.log("need to add dialog support");
10141                 /*
10142                 Roo.MessageBox.alert("Error",
10143                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10144                         action.result.errorMsg :
10145                         "Saving Failed, please check your entries or try again"
10146                 );
10147                 */
10148             }
10149
10150             this.fireEvent('actionfailed', this, action);
10151         }
10152
10153     },
10154     /**
10155      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10156      * @param {String} id The value to search for
10157      * @return Field
10158      */
10159     findField : function(id){
10160         var items = this.getItems();
10161         var field = items.get(id);
10162         if(!field){
10163              items.each(function(f){
10164                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10165                     field = f;
10166                     return false;
10167                 }
10168                 return true;
10169             });
10170         }
10171         return field || null;
10172     },
10173      /**
10174      * Mark fields in this form invalid in bulk.
10175      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10176      * @return {BasicForm} this
10177      */
10178     markInvalid : function(errors){
10179         if(errors instanceof Array){
10180             for(var i = 0, len = errors.length; i < len; i++){
10181                 var fieldError = errors[i];
10182                 var f = this.findField(fieldError.id);
10183                 if(f){
10184                     f.markInvalid(fieldError.msg);
10185                 }
10186             }
10187         }else{
10188             var field, id;
10189             for(id in errors){
10190                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10191                     field.markInvalid(errors[id]);
10192                 }
10193             }
10194         }
10195         //Roo.each(this.childForms || [], function (f) {
10196         //    f.markInvalid(errors);
10197         //});
10198
10199         return this;
10200     },
10201
10202     /**
10203      * Set values for fields in this form in bulk.
10204      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10205      * @return {BasicForm} this
10206      */
10207     setValues : function(values){
10208         if(values instanceof Array){ // array of objects
10209             for(var i = 0, len = values.length; i < len; i++){
10210                 var v = values[i];
10211                 var f = this.findField(v.id);
10212                 if(f){
10213                     f.setValue(v.value);
10214                     if(this.trackResetOnLoad){
10215                         f.originalValue = f.getValue();
10216                     }
10217                 }
10218             }
10219         }else{ // object hash
10220             var field, id;
10221             for(id in values){
10222                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10223
10224                     if (field.setFromData &&
10225                         field.valueField &&
10226                         field.displayField &&
10227                         // combos' with local stores can
10228                         // be queried via setValue()
10229                         // to set their value..
10230                         (field.store && !field.store.isLocal)
10231                         ) {
10232                         // it's a combo
10233                         var sd = { };
10234                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10235                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10236                         field.setFromData(sd);
10237
10238                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10239                         
10240                         field.setFromData(values);
10241                         
10242                     } else {
10243                         field.setValue(values[id]);
10244                     }
10245
10246
10247                     if(this.trackResetOnLoad){
10248                         field.originalValue = field.getValue();
10249                     }
10250                 }
10251             }
10252         }
10253
10254         //Roo.each(this.childForms || [], function (f) {
10255         //    f.setValues(values);
10256         //});
10257
10258         return this;
10259     },
10260
10261     /**
10262      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10263      * they are returned as an array.
10264      * @param {Boolean} asString
10265      * @return {Object}
10266      */
10267     getValues : function(asString){
10268         //if (this.childForms) {
10269             // copy values from the child forms
10270         //    Roo.each(this.childForms, function (f) {
10271         //        this.setValues(f.getValues());
10272         //    }, this);
10273         //}
10274
10275
10276
10277         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10278         if(asString === true){
10279             return fs;
10280         }
10281         return Roo.urlDecode(fs);
10282     },
10283
10284     /**
10285      * Returns the fields in this form as an object with key/value pairs.
10286      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10287      * @return {Object}
10288      */
10289     getFieldValues : function(with_hidden)
10290     {
10291         var items = this.getItems();
10292         var ret = {};
10293         items.each(function(f){
10294             
10295             if (!f.getName()) {
10296                 return;
10297             }
10298             
10299             var v = f.getValue();
10300             
10301             if (f.inputType =='radio') {
10302                 if (typeof(ret[f.getName()]) == 'undefined') {
10303                     ret[f.getName()] = ''; // empty..
10304                 }
10305
10306                 if (!f.el.dom.checked) {
10307                     return;
10308
10309                 }
10310                 v = f.el.dom.value;
10311
10312             }
10313             
10314             if(f.xtype == 'MoneyField'){
10315                 ret[f.currencyName] = f.getCurrency();
10316             }
10317
10318             // not sure if this supported any more..
10319             if ((typeof(v) == 'object') && f.getRawValue) {
10320                 v = f.getRawValue() ; // dates..
10321             }
10322             // combo boxes where name != hiddenName...
10323             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10324                 ret[f.name] = f.getRawValue();
10325             }
10326             ret[f.getName()] = v;
10327         });
10328
10329         return ret;
10330     },
10331
10332     /**
10333      * Clears all invalid messages in this form.
10334      * @return {BasicForm} this
10335      */
10336     clearInvalid : function(){
10337         var items = this.getItems();
10338
10339         items.each(function(f){
10340            f.clearInvalid();
10341         });
10342
10343         return this;
10344     },
10345
10346     /**
10347      * Resets this form.
10348      * @return {BasicForm} this
10349      */
10350     reset : function(){
10351         var items = this.getItems();
10352         items.each(function(f){
10353             f.reset();
10354         });
10355
10356         Roo.each(this.childForms || [], function (f) {
10357             f.reset();
10358         });
10359
10360
10361         return this;
10362     },
10363     
10364     getItems : function()
10365     {
10366         var r=new Roo.util.MixedCollection(false, function(o){
10367             return o.id || (o.id = Roo.id());
10368         });
10369         var iter = function(el) {
10370             if (el.inputEl) {
10371                 r.add(el);
10372             }
10373             if (!el.items) {
10374                 return;
10375             }
10376             Roo.each(el.items,function(e) {
10377                 iter(e);
10378             });
10379         };
10380
10381         iter(this);
10382         return r;
10383     },
10384     
10385     hideFields : function(items)
10386     {
10387         Roo.each(items, function(i){
10388             
10389             var f = this.findField(i);
10390             
10391             if(!f){
10392                 return;
10393             }
10394             
10395             f.hide();
10396             
10397         }, this);
10398     },
10399     
10400     showFields : function(items)
10401     {
10402         Roo.each(items, function(i){
10403             
10404             var f = this.findField(i);
10405             
10406             if(!f){
10407                 return;
10408             }
10409             
10410             f.show();
10411             
10412         }, this);
10413     }
10414
10415 });
10416
10417 Roo.apply(Roo.bootstrap.Form, {
10418     
10419     popover : {
10420         
10421         padding : 5,
10422         
10423         isApplied : false,
10424         
10425         isMasked : false,
10426         
10427         form : false,
10428         
10429         target : false,
10430         
10431         toolTip : false,
10432         
10433         intervalID : false,
10434         
10435         maskEl : false,
10436         
10437         apply : function()
10438         {
10439             if(this.isApplied){
10440                 return;
10441             }
10442             
10443             this.maskEl = {
10444                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10445                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10446                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10447                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10448             };
10449             
10450             this.maskEl.top.enableDisplayMode("block");
10451             this.maskEl.left.enableDisplayMode("block");
10452             this.maskEl.bottom.enableDisplayMode("block");
10453             this.maskEl.right.enableDisplayMode("block");
10454             
10455             this.toolTip = new Roo.bootstrap.Tooltip({
10456                 cls : 'roo-form-error-popover',
10457                 alignment : {
10458                     'left' : ['r-l', [-2,0], 'right'],
10459                     'right' : ['l-r', [2,0], 'left'],
10460                     'bottom' : ['tl-bl', [0,2], 'top'],
10461                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10462                 }
10463             });
10464             
10465             this.toolTip.render(Roo.get(document.body));
10466
10467             this.toolTip.el.enableDisplayMode("block");
10468             
10469             Roo.get(document.body).on('click', function(){
10470                 this.unmask();
10471             }, this);
10472             
10473             Roo.get(document.body).on('touchstart', function(){
10474                 this.unmask();
10475             }, this);
10476             
10477             this.isApplied = true
10478         },
10479         
10480         mask : function(form, target)
10481         {
10482             this.form = form;
10483             
10484             this.target = target;
10485             
10486             if(!this.form.errorMask || !target.el){
10487                 return;
10488             }
10489             
10490             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10491             
10492             Roo.log(scrollable);
10493             
10494             var ot = this.target.el.calcOffsetsTo(scrollable);
10495             
10496             var scrollTo = ot[1] - this.form.maskOffset;
10497             
10498             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10499             
10500             scrollable.scrollTo('top', scrollTo);
10501             
10502             var box = this.target.el.getBox();
10503             Roo.log(box);
10504             var zIndex = Roo.bootstrap.Modal.zIndex++;
10505
10506             
10507             this.maskEl.top.setStyle('position', 'absolute');
10508             this.maskEl.top.setStyle('z-index', zIndex);
10509             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10510             this.maskEl.top.setLeft(0);
10511             this.maskEl.top.setTop(0);
10512             this.maskEl.top.show();
10513             
10514             this.maskEl.left.setStyle('position', 'absolute');
10515             this.maskEl.left.setStyle('z-index', zIndex);
10516             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10517             this.maskEl.left.setLeft(0);
10518             this.maskEl.left.setTop(box.y - this.padding);
10519             this.maskEl.left.show();
10520
10521             this.maskEl.bottom.setStyle('position', 'absolute');
10522             this.maskEl.bottom.setStyle('z-index', zIndex);
10523             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10524             this.maskEl.bottom.setLeft(0);
10525             this.maskEl.bottom.setTop(box.bottom + this.padding);
10526             this.maskEl.bottom.show();
10527
10528             this.maskEl.right.setStyle('position', 'absolute');
10529             this.maskEl.right.setStyle('z-index', zIndex);
10530             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10531             this.maskEl.right.setLeft(box.right + this.padding);
10532             this.maskEl.right.setTop(box.y - this.padding);
10533             this.maskEl.right.show();
10534
10535             this.toolTip.bindEl = this.target.el;
10536
10537             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10538
10539             var tip = this.target.blankText;
10540
10541             if(this.target.getValue() !== '' ) {
10542                 
10543                 if (this.target.invalidText.length) {
10544                     tip = this.target.invalidText;
10545                 } else if (this.target.regexText.length){
10546                     tip = this.target.regexText;
10547                 }
10548             }
10549
10550             this.toolTip.show(tip);
10551
10552             this.intervalID = window.setInterval(function() {
10553                 Roo.bootstrap.Form.popover.unmask();
10554             }, 10000);
10555
10556             window.onwheel = function(){ return false;};
10557             
10558             (function(){ this.isMasked = true; }).defer(500, this);
10559             
10560         },
10561         
10562         unmask : function()
10563         {
10564             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10565                 return;
10566             }
10567             
10568             this.maskEl.top.setStyle('position', 'absolute');
10569             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10570             this.maskEl.top.hide();
10571
10572             this.maskEl.left.setStyle('position', 'absolute');
10573             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10574             this.maskEl.left.hide();
10575
10576             this.maskEl.bottom.setStyle('position', 'absolute');
10577             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10578             this.maskEl.bottom.hide();
10579
10580             this.maskEl.right.setStyle('position', 'absolute');
10581             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10582             this.maskEl.right.hide();
10583             
10584             this.toolTip.hide();
10585             
10586             this.toolTip.el.hide();
10587             
10588             window.onwheel = function(){ return true;};
10589             
10590             if(this.intervalID){
10591                 window.clearInterval(this.intervalID);
10592                 this.intervalID = false;
10593             }
10594             
10595             this.isMasked = false;
10596             
10597         }
10598         
10599     }
10600     
10601 });
10602
10603 /*
10604  * Based on:
10605  * Ext JS Library 1.1.1
10606  * Copyright(c) 2006-2007, Ext JS, LLC.
10607  *
10608  * Originally Released Under LGPL - original licence link has changed is not relivant.
10609  *
10610  * Fork - LGPL
10611  * <script type="text/javascript">
10612  */
10613 /**
10614  * @class Roo.form.VTypes
10615  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10616  * @singleton
10617  */
10618 Roo.form.VTypes = function(){
10619     // closure these in so they are only created once.
10620     var alpha = /^[a-zA-Z_]+$/;
10621     var alphanum = /^[a-zA-Z0-9_]+$/;
10622     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10623     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10624
10625     // All these messages and functions are configurable
10626     return {
10627         /**
10628          * The function used to validate email addresses
10629          * @param {String} value The email address
10630          */
10631         'email' : function(v){
10632             return email.test(v);
10633         },
10634         /**
10635          * The error text to display when the email validation function returns false
10636          * @type String
10637          */
10638         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10639         /**
10640          * The keystroke filter mask to be applied on email input
10641          * @type RegExp
10642          */
10643         'emailMask' : /[a-z0-9_\.\-@]/i,
10644
10645         /**
10646          * The function used to validate URLs
10647          * @param {String} value The URL
10648          */
10649         'url' : function(v){
10650             return url.test(v);
10651         },
10652         /**
10653          * The error text to display when the url validation function returns false
10654          * @type String
10655          */
10656         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10657         
10658         /**
10659          * The function used to validate alpha values
10660          * @param {String} value The value
10661          */
10662         'alpha' : function(v){
10663             return alpha.test(v);
10664         },
10665         /**
10666          * The error text to display when the alpha validation function returns false
10667          * @type String
10668          */
10669         'alphaText' : 'This field should only contain letters and _',
10670         /**
10671          * The keystroke filter mask to be applied on alpha input
10672          * @type RegExp
10673          */
10674         'alphaMask' : /[a-z_]/i,
10675
10676         /**
10677          * The function used to validate alphanumeric values
10678          * @param {String} value The value
10679          */
10680         'alphanum' : function(v){
10681             return alphanum.test(v);
10682         },
10683         /**
10684          * The error text to display when the alphanumeric validation function returns false
10685          * @type String
10686          */
10687         'alphanumText' : 'This field should only contain letters, numbers and _',
10688         /**
10689          * The keystroke filter mask to be applied on alphanumeric input
10690          * @type RegExp
10691          */
10692         'alphanumMask' : /[a-z0-9_]/i
10693     };
10694 }();/*
10695  * - LGPL
10696  *
10697  * Input
10698  * 
10699  */
10700
10701 /**
10702  * @class Roo.bootstrap.Input
10703  * @extends Roo.bootstrap.Component
10704  * Bootstrap Input class
10705  * @cfg {Boolean} disabled is it disabled
10706  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10707  * @cfg {String} name name of the input
10708  * @cfg {string} fieldLabel - the label associated
10709  * @cfg {string} placeholder - placeholder to put in text.
10710  * @cfg {string}  before - input group add on before
10711  * @cfg {string} after - input group add on after
10712  * @cfg {string} size - (lg|sm) or leave empty..
10713  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10714  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10715  * @cfg {Number} md colspan out of 12 for computer-sized screens
10716  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10717  * @cfg {string} value default value of the input
10718  * @cfg {Number} labelWidth set the width of label 
10719  * @cfg {Number} labellg set the width of label (1-12)
10720  * @cfg {Number} labelmd set the width of label (1-12)
10721  * @cfg {Number} labelsm set the width of label (1-12)
10722  * @cfg {Number} labelxs set the width of label (1-12)
10723  * @cfg {String} labelAlign (top|left)
10724  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10725  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10726  * @cfg {String} indicatorpos (left|right) default left
10727  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10728  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10729  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10730
10731  * @cfg {String} align (left|center|right) Default left
10732  * @cfg {Boolean} forceFeedback (true|false) Default false
10733  * 
10734  * @constructor
10735  * Create a new Input
10736  * @param {Object} config The config object
10737  */
10738
10739 Roo.bootstrap.Input = function(config){
10740     
10741     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10742     
10743     this.addEvents({
10744         /**
10745          * @event focus
10746          * Fires when this field receives input focus.
10747          * @param {Roo.form.Field} this
10748          */
10749         focus : true,
10750         /**
10751          * @event blur
10752          * Fires when this field loses input focus.
10753          * @param {Roo.form.Field} this
10754          */
10755         blur : true,
10756         /**
10757          * @event specialkey
10758          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10759          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10760          * @param {Roo.form.Field} this
10761          * @param {Roo.EventObject} e The event object
10762          */
10763         specialkey : true,
10764         /**
10765          * @event change
10766          * Fires just before the field blurs if the field value has changed.
10767          * @param {Roo.form.Field} this
10768          * @param {Mixed} newValue The new value
10769          * @param {Mixed} oldValue The original value
10770          */
10771         change : true,
10772         /**
10773          * @event invalid
10774          * Fires after the field has been marked as invalid.
10775          * @param {Roo.form.Field} this
10776          * @param {String} msg The validation message
10777          */
10778         invalid : true,
10779         /**
10780          * @event valid
10781          * Fires after the field has been validated with no errors.
10782          * @param {Roo.form.Field} this
10783          */
10784         valid : true,
10785          /**
10786          * @event keyup
10787          * Fires after the key up
10788          * @param {Roo.form.Field} this
10789          * @param {Roo.EventObject}  e The event Object
10790          */
10791         keyup : true,
10792         /**
10793          * @event paste
10794          * Fires after the user pastes into input
10795          * @param {Roo.form.Field} this
10796          * @param {Roo.EventObject}  e The event Object
10797          */
10798         paste : true
10799     });
10800 };
10801
10802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10803      /**
10804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10805       automatic validation (defaults to "keyup").
10806      */
10807     validationEvent : "keyup",
10808      /**
10809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10810      */
10811     validateOnBlur : true,
10812     /**
10813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10814      */
10815     validationDelay : 250,
10816      /**
10817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10818      */
10819     focusClass : "x-form-focus",  // not needed???
10820     
10821        
10822     /**
10823      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10824      */
10825     invalidClass : "has-warning",
10826     
10827     /**
10828      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10829      */
10830     validClass : "has-success",
10831     
10832     /**
10833      * @cfg {Boolean} hasFeedback (true|false) default true
10834      */
10835     hasFeedback : true,
10836     
10837     /**
10838      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10839      */
10840     invalidFeedbackClass : "glyphicon-warning-sign",
10841     
10842     /**
10843      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10844      */
10845     validFeedbackClass : "glyphicon-ok",
10846     
10847     /**
10848      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10849      */
10850     selectOnFocus : false,
10851     
10852      /**
10853      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10854      */
10855     maskRe : null,
10856        /**
10857      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10858      */
10859     vtype : null,
10860     
10861       /**
10862      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10863      */
10864     disableKeyFilter : false,
10865     
10866        /**
10867      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10868      */
10869     disabled : false,
10870      /**
10871      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10872      */
10873     allowBlank : true,
10874     /**
10875      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10876      */
10877     blankText : "Please complete this mandatory field",
10878     
10879      /**
10880      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10881      */
10882     minLength : 0,
10883     /**
10884      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10885      */
10886     maxLength : Number.MAX_VALUE,
10887     /**
10888      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10889      */
10890     minLengthText : "The minimum length for this field is {0}",
10891     /**
10892      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10893      */
10894     maxLengthText : "The maximum length for this field is {0}",
10895   
10896     
10897     /**
10898      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10899      * If available, this function will be called only after the basic validators all return true, and will be passed the
10900      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10901      */
10902     validator : null,
10903     /**
10904      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10905      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10906      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10907      */
10908     regex : null,
10909     /**
10910      * @cfg {String} regexText -- Depricated - use Invalid Text
10911      */
10912     regexText : "",
10913     
10914     /**
10915      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10916      */
10917     invalidText : "",
10918     
10919     
10920     
10921     autocomplete: false,
10922     
10923     
10924     fieldLabel : '',
10925     inputType : 'text',
10926     
10927     name : false,
10928     placeholder: false,
10929     before : false,
10930     after : false,
10931     size : false,
10932     hasFocus : false,
10933     preventMark: false,
10934     isFormField : true,
10935     value : '',
10936     labelWidth : 2,
10937     labelAlign : false,
10938     readOnly : false,
10939     align : false,
10940     formatedValue : false,
10941     forceFeedback : false,
10942     
10943     indicatorpos : 'left',
10944     
10945     labellg : 0,
10946     labelmd : 0,
10947     labelsm : 0,
10948     labelxs : 0,
10949     
10950     capture : '',
10951     accept : '',
10952     
10953     parentLabelAlign : function()
10954     {
10955         var parent = this;
10956         while (parent.parent()) {
10957             parent = parent.parent();
10958             if (typeof(parent.labelAlign) !='undefined') {
10959                 return parent.labelAlign;
10960             }
10961         }
10962         return 'left';
10963         
10964     },
10965     
10966     getAutoCreate : function()
10967     {
10968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10969         
10970         var id = Roo.id();
10971         
10972         var cfg = {};
10973         
10974         if(this.inputType != 'hidden'){
10975             cfg.cls = 'form-group' //input-group
10976         }
10977         
10978         var input =  {
10979             tag: 'input',
10980             id : id,
10981             type : this.inputType,
10982             value : this.value,
10983             cls : 'form-control',
10984             placeholder : this.placeholder || '',
10985             autocomplete : this.autocomplete || 'new-password'
10986         };
10987         if (this.inputType == 'file') {
10988             input.style = 'overflow:hidden'; // why not in CSS?
10989         }
10990         
10991         if(this.capture.length){
10992             input.capture = this.capture;
10993         }
10994         
10995         if(this.accept.length){
10996             input.accept = this.accept + "/*";
10997         }
10998         
10999         if(this.align){
11000             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11001         }
11002         
11003         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11004             input.maxLength = this.maxLength;
11005         }
11006         
11007         if (this.disabled) {
11008             input.disabled=true;
11009         }
11010         
11011         if (this.readOnly) {
11012             input.readonly=true;
11013         }
11014         
11015         if (this.name) {
11016             input.name = this.name;
11017         }
11018         
11019         if (this.size) {
11020             input.cls += ' input-' + this.size;
11021         }
11022         
11023         var settings=this;
11024         ['xs','sm','md','lg'].map(function(size){
11025             if (settings[size]) {
11026                 cfg.cls += ' col-' + size + '-' + settings[size];
11027             }
11028         });
11029         
11030         var inputblock = input;
11031         
11032         var feedback = {
11033             tag: 'span',
11034             cls: 'glyphicon form-control-feedback'
11035         };
11036             
11037         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11038             
11039             inputblock = {
11040                 cls : 'has-feedback',
11041                 cn :  [
11042                     input,
11043                     feedback
11044                 ] 
11045             };  
11046         }
11047         
11048         if (this.before || this.after) {
11049             
11050             inputblock = {
11051                 cls : 'input-group',
11052                 cn :  [] 
11053             };
11054             
11055             if (this.before && typeof(this.before) == 'string') {
11056                 
11057                 inputblock.cn.push({
11058                     tag :'span',
11059                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11060                     html : this.before
11061                 });
11062             }
11063             if (this.before && typeof(this.before) == 'object') {
11064                 this.before = Roo.factory(this.before);
11065                 
11066                 inputblock.cn.push({
11067                     tag :'span',
11068                     cls : 'roo-input-before input-group-prepend   input-group-' +
11069                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11070                 });
11071             }
11072             
11073             inputblock.cn.push(input);
11074             
11075             if (this.after && typeof(this.after) == 'string') {
11076                 inputblock.cn.push({
11077                     tag :'span',
11078                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11079                     html : this.after
11080                 });
11081             }
11082             if (this.after && typeof(this.after) == 'object') {
11083                 this.after = Roo.factory(this.after);
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-after input-group-append  input-group-' +
11088                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11089                 });
11090             }
11091             
11092             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11093                 inputblock.cls += ' has-feedback';
11094                 inputblock.cn.push(feedback);
11095             }
11096         };
11097         var indicator = {
11098             tag : 'i',
11099             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11100             tooltip : 'This field is required'
11101         };
11102         if (this.allowBlank ) {
11103             indicator.style = this.allowBlank ? ' display:none' : '';
11104         }
11105         if (align ==='left' && this.fieldLabel.length) {
11106             
11107             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11108             
11109             cfg.cn = [
11110                 indicator,
11111                 {
11112                     tag: 'label',
11113                     'for' :  id,
11114                     cls : 'control-label col-form-label',
11115                     html : this.fieldLabel
11116
11117                 },
11118                 {
11119                     cls : "", 
11120                     cn: [
11121                         inputblock
11122                     ]
11123                 }
11124             ];
11125             
11126             var labelCfg = cfg.cn[1];
11127             var contentCfg = cfg.cn[2];
11128             
11129             if(this.indicatorpos == 'right'){
11130                 cfg.cn = [
11131                     {
11132                         tag: 'label',
11133                         'for' :  id,
11134                         cls : 'control-label col-form-label',
11135                         cn : [
11136                             {
11137                                 tag : 'span',
11138                                 html : this.fieldLabel
11139                             },
11140                             indicator
11141                         ]
11142                     },
11143                     {
11144                         cls : "",
11145                         cn: [
11146                             inputblock
11147                         ]
11148                     }
11149
11150                 ];
11151                 
11152                 labelCfg = cfg.cn[0];
11153                 contentCfg = cfg.cn[1];
11154             
11155             }
11156             
11157             if(this.labelWidth > 12){
11158                 labelCfg.style = "width: " + this.labelWidth + 'px';
11159             }
11160             
11161             if(this.labelWidth < 13 && this.labelmd == 0){
11162                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11163             }
11164             
11165             if(this.labellg > 0){
11166                 labelCfg.cls += ' col-lg-' + this.labellg;
11167                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11168             }
11169             
11170             if(this.labelmd > 0){
11171                 labelCfg.cls += ' col-md-' + this.labelmd;
11172                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11173             }
11174             
11175             if(this.labelsm > 0){
11176                 labelCfg.cls += ' col-sm-' + this.labelsm;
11177                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11178             }
11179             
11180             if(this.labelxs > 0){
11181                 labelCfg.cls += ' col-xs-' + this.labelxs;
11182                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11183             }
11184             
11185             
11186         } else if ( this.fieldLabel.length) {
11187                 
11188             
11189             
11190             cfg.cn = [
11191                 {
11192                     tag : 'i',
11193                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11194                     tooltip : 'This field is required',
11195                     style : this.allowBlank ? ' display:none' : '' 
11196                 },
11197                 {
11198                     tag: 'label',
11199                    //cls : 'input-group-addon',
11200                     html : this.fieldLabel
11201
11202                 },
11203
11204                inputblock
11205
11206            ];
11207            
11208            if(this.indicatorpos == 'right'){
11209        
11210                 cfg.cn = [
11211                     {
11212                         tag: 'label',
11213                        //cls : 'input-group-addon',
11214                         html : this.fieldLabel
11215
11216                     },
11217                     {
11218                         tag : 'i',
11219                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11220                         tooltip : 'This field is required',
11221                         style : this.allowBlank ? ' display:none' : '' 
11222                     },
11223
11224                    inputblock
11225
11226                ];
11227
11228             }
11229
11230         } else {
11231             
11232             cfg.cn = [
11233
11234                     inputblock
11235
11236             ];
11237                 
11238                 
11239         };
11240         
11241         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11242            cfg.cls += ' navbar-form';
11243         }
11244         
11245         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11246             // on BS4 we do this only if not form 
11247             cfg.cls += ' navbar-form';
11248             cfg.tag = 'li';
11249         }
11250         
11251         return cfg;
11252         
11253     },
11254     /**
11255      * return the real input element.
11256      */
11257     inputEl: function ()
11258     {
11259         return this.el.select('input.form-control',true).first();
11260     },
11261     
11262     tooltipEl : function()
11263     {
11264         return this.inputEl();
11265     },
11266     
11267     indicatorEl : function()
11268     {
11269         if (Roo.bootstrap.version == 4) {
11270             return false; // not enabled in v4 yet.
11271         }
11272         
11273         var indicator = this.el.select('i.roo-required-indicator',true).first();
11274         
11275         if(!indicator){
11276             return false;
11277         }
11278         
11279         return indicator;
11280         
11281     },
11282     
11283     setDisabled : function(v)
11284     {
11285         var i  = this.inputEl().dom;
11286         if (!v) {
11287             i.removeAttribute('disabled');
11288             return;
11289             
11290         }
11291         i.setAttribute('disabled','true');
11292     },
11293     initEvents : function()
11294     {
11295           
11296         this.inputEl().on("keydown" , this.fireKey,  this);
11297         this.inputEl().on("focus", this.onFocus,  this);
11298         this.inputEl().on("blur", this.onBlur,  this);
11299         
11300         this.inputEl().relayEvent('keyup', this);
11301         this.inputEl().relayEvent('paste', this);
11302         
11303         this.indicator = this.indicatorEl();
11304         
11305         if(this.indicator){
11306             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11307         }
11308  
11309         // reference to original value for reset
11310         this.originalValue = this.getValue();
11311         //Roo.form.TextField.superclass.initEvents.call(this);
11312         if(this.validationEvent == 'keyup'){
11313             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11314             this.inputEl().on('keyup', this.filterValidation, this);
11315         }
11316         else if(this.validationEvent !== false){
11317             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11318         }
11319         
11320         if(this.selectOnFocus){
11321             this.on("focus", this.preFocus, this);
11322             
11323         }
11324         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11325             this.inputEl().on("keypress", this.filterKeys, this);
11326         } else {
11327             this.inputEl().relayEvent('keypress', this);
11328         }
11329        /* if(this.grow){
11330             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11331             this.el.on("click", this.autoSize,  this);
11332         }
11333         */
11334         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11335             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11336         }
11337         
11338         if (typeof(this.before) == 'object') {
11339             this.before.render(this.el.select('.roo-input-before',true).first());
11340         }
11341         if (typeof(this.after) == 'object') {
11342             this.after.render(this.el.select('.roo-input-after',true).first());
11343         }
11344         
11345         this.inputEl().on('change', this.onChange, this);
11346         
11347     },
11348     filterValidation : function(e){
11349         if(!e.isNavKeyPress()){
11350             this.validationTask.delay(this.validationDelay);
11351         }
11352     },
11353      /**
11354      * Validates the field value
11355      * @return {Boolean} True if the value is valid, else false
11356      */
11357     validate : function(){
11358         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11359         if(this.disabled || this.validateValue(this.getRawValue())){
11360             this.markValid();
11361             return true;
11362         }
11363         
11364         this.markInvalid();
11365         return false;
11366     },
11367     
11368     
11369     /**
11370      * Validates a value according to the field's validation rules and marks the field as invalid
11371      * if the validation fails
11372      * @param {Mixed} value The value to validate
11373      * @return {Boolean} True if the value is valid, else false
11374      */
11375     validateValue : function(value)
11376     {
11377         if(this.getVisibilityEl().hasClass('hidden')){
11378             return true;
11379         }
11380         
11381         if(value.length < 1)  { // if it's blank
11382             if(this.allowBlank){
11383                 return true;
11384             }
11385             return false;
11386         }
11387         
11388         if(value.length < this.minLength){
11389             return false;
11390         }
11391         if(value.length > this.maxLength){
11392             return false;
11393         }
11394         if(this.vtype){
11395             var vt = Roo.form.VTypes;
11396             if(!vt[this.vtype](value, this)){
11397                 return false;
11398             }
11399         }
11400         if(typeof this.validator == "function"){
11401             var msg = this.validator(value);
11402             if(msg !== true){
11403                 return false;
11404             }
11405             if (typeof(msg) == 'string') {
11406                 this.invalidText = msg;
11407             }
11408         }
11409         
11410         if(this.regex && !this.regex.test(value)){
11411             return false;
11412         }
11413         
11414         return true;
11415     },
11416     
11417      // private
11418     fireKey : function(e){
11419         //Roo.log('field ' + e.getKey());
11420         if(e.isNavKeyPress()){
11421             this.fireEvent("specialkey", this, e);
11422         }
11423     },
11424     focus : function (selectText){
11425         if(this.rendered){
11426             this.inputEl().focus();
11427             if(selectText === true){
11428                 this.inputEl().dom.select();
11429             }
11430         }
11431         return this;
11432     } ,
11433     
11434     onFocus : function(){
11435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436            // this.el.addClass(this.focusClass);
11437         }
11438         if(!this.hasFocus){
11439             this.hasFocus = true;
11440             this.startValue = this.getValue();
11441             this.fireEvent("focus", this);
11442         }
11443     },
11444     
11445     beforeBlur : Roo.emptyFn,
11446
11447     
11448     // private
11449     onBlur : function(){
11450         this.beforeBlur();
11451         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11452             //this.el.removeClass(this.focusClass);
11453         }
11454         this.hasFocus = false;
11455         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11456             this.validate();
11457         }
11458         var v = this.getValue();
11459         if(String(v) !== String(this.startValue)){
11460             this.fireEvent('change', this, v, this.startValue);
11461         }
11462         this.fireEvent("blur", this);
11463     },
11464     
11465     onChange : function(e)
11466     {
11467         var v = this.getValue();
11468         if(String(v) !== String(this.startValue)){
11469             this.fireEvent('change', this, v, this.startValue);
11470         }
11471         
11472     },
11473     
11474     /**
11475      * Resets the current field value to the originally loaded value and clears any validation messages
11476      */
11477     reset : function(){
11478         this.setValue(this.originalValue);
11479         this.validate();
11480     },
11481      /**
11482      * Returns the name of the field
11483      * @return {Mixed} name The name field
11484      */
11485     getName: function(){
11486         return this.name;
11487     },
11488      /**
11489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11490      * @return {Mixed} value The field value
11491      */
11492     getValue : function(){
11493         
11494         var v = this.inputEl().getValue();
11495         
11496         return v;
11497     },
11498     /**
11499      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11500      * @return {Mixed} value The field value
11501      */
11502     getRawValue : function(){
11503         var v = this.inputEl().getValue();
11504         
11505         return v;
11506     },
11507     
11508     /**
11509      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11510      * @param {Mixed} value The value to set
11511      */
11512     setRawValue : function(v){
11513         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11514     },
11515     
11516     selectText : function(start, end){
11517         var v = this.getRawValue();
11518         if(v.length > 0){
11519             start = start === undefined ? 0 : start;
11520             end = end === undefined ? v.length : end;
11521             var d = this.inputEl().dom;
11522             if(d.setSelectionRange){
11523                 d.setSelectionRange(start, end);
11524             }else if(d.createTextRange){
11525                 var range = d.createTextRange();
11526                 range.moveStart("character", start);
11527                 range.moveEnd("character", v.length-end);
11528                 range.select();
11529             }
11530         }
11531     },
11532     
11533     /**
11534      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11535      * @param {Mixed} value The value to set
11536      */
11537     setValue : function(v){
11538         this.value = v;
11539         if(this.rendered){
11540             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11541             this.validate();
11542         }
11543     },
11544     
11545     /*
11546     processValue : function(value){
11547         if(this.stripCharsRe){
11548             var newValue = value.replace(this.stripCharsRe, '');
11549             if(newValue !== value){
11550                 this.setRawValue(newValue);
11551                 return newValue;
11552             }
11553         }
11554         return value;
11555     },
11556   */
11557     preFocus : function(){
11558         
11559         if(this.selectOnFocus){
11560             this.inputEl().dom.select();
11561         }
11562     },
11563     filterKeys : function(e){
11564         var k = e.getKey();
11565         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11566             return;
11567         }
11568         var c = e.getCharCode(), cc = String.fromCharCode(c);
11569         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11570             return;
11571         }
11572         if(!this.maskRe.test(cc)){
11573             e.stopEvent();
11574         }
11575     },
11576      /**
11577      * Clear any invalid styles/messages for this field
11578      */
11579     clearInvalid : function(){
11580         
11581         if(!this.el || this.preventMark){ // not rendered
11582             return;
11583         }
11584         
11585         
11586         this.el.removeClass([this.invalidClass, 'is-invalid']);
11587         
11588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11589             
11590             var feedback = this.el.select('.form-control-feedback', true).first();
11591             
11592             if(feedback){
11593                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11594             }
11595             
11596         }
11597         
11598         if(this.indicator){
11599             this.indicator.removeClass('visible');
11600             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11601         }
11602         
11603         this.fireEvent('valid', this);
11604     },
11605     
11606      /**
11607      * Mark this field as valid
11608      */
11609     markValid : function()
11610     {
11611         if(!this.el  || this.preventMark){ // not rendered...
11612             return;
11613         }
11614         
11615         this.el.removeClass([this.invalidClass, this.validClass]);
11616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617
11618         var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620         if(feedback){
11621             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622         }
11623         
11624         if(this.indicator){
11625             this.indicator.removeClass('visible');
11626             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11627         }
11628         
11629         if(this.disabled){
11630             return;
11631         }
11632         
11633            
11634         if(this.allowBlank && !this.getRawValue().length){
11635             return;
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.validClass);
11639         } else {
11640             this.inputEl().addClass('is-valid');
11641         }
11642
11643         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11644             
11645             var feedback = this.el.select('.form-control-feedback', true).first();
11646             
11647             if(feedback){
11648                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650             }
11651             
11652         }
11653         
11654         this.fireEvent('valid', this);
11655     },
11656     
11657      /**
11658      * Mark this field as invalid
11659      * @param {String} msg The validation message
11660      */
11661     markInvalid : function(msg)
11662     {
11663         if(!this.el  || this.preventMark){ // not rendered
11664             return;
11665         }
11666         
11667         this.el.removeClass([this.invalidClass, this.validClass]);
11668         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11669         
11670         var feedback = this.el.select('.form-control-feedback', true).first();
11671             
11672         if(feedback){
11673             this.el.select('.form-control-feedback', true).first().removeClass(
11674                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11675         }
11676
11677         if(this.disabled){
11678             return;
11679         }
11680         
11681         if(this.allowBlank && !this.getRawValue().length){
11682             return;
11683         }
11684         
11685         if(this.indicator){
11686             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11687             this.indicator.addClass('visible');
11688         }
11689         if (Roo.bootstrap.version == 3) {
11690             this.el.addClass(this.invalidClass);
11691         } else {
11692             this.inputEl().addClass('is-invalid');
11693         }
11694         
11695         
11696         
11697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11698             
11699             var feedback = this.el.select('.form-control-feedback', true).first();
11700             
11701             if(feedback){
11702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11703                 
11704                 if(this.getValue().length || this.forceFeedback){
11705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11706                 }
11707                 
11708             }
11709             
11710         }
11711         
11712         this.fireEvent('invalid', this, msg);
11713     },
11714     // private
11715     SafariOnKeyDown : function(event)
11716     {
11717         // this is a workaround for a password hang bug on chrome/ webkit.
11718         if (this.inputEl().dom.type != 'password') {
11719             return;
11720         }
11721         
11722         var isSelectAll = false;
11723         
11724         if(this.inputEl().dom.selectionEnd > 0){
11725             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11726         }
11727         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11728             event.preventDefault();
11729             this.setValue('');
11730             return;
11731         }
11732         
11733         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11734             
11735             event.preventDefault();
11736             // this is very hacky as keydown always get's upper case.
11737             //
11738             var cc = String.fromCharCode(event.getCharCode());
11739             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11740             
11741         }
11742     },
11743     adjustWidth : function(tag, w){
11744         tag = tag.toLowerCase();
11745         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11746             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11747                 if(tag == 'input'){
11748                     return w + 2;
11749                 }
11750                 if(tag == 'textarea'){
11751                     return w-2;
11752                 }
11753             }else if(Roo.isOpera){
11754                 if(tag == 'input'){
11755                     return w + 2;
11756                 }
11757                 if(tag == 'textarea'){
11758                     return w-2;
11759                 }
11760             }
11761         }
11762         return w;
11763     },
11764     
11765     setFieldLabel : function(v)
11766     {
11767         if(!this.rendered){
11768             return;
11769         }
11770         
11771         if(this.indicatorEl()){
11772             var ar = this.el.select('label > span',true);
11773             
11774             if (ar.elements.length) {
11775                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11776                 this.fieldLabel = v;
11777                 return;
11778             }
11779             
11780             var br = this.el.select('label',true);
11781             
11782             if(br.elements.length) {
11783                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784                 this.fieldLabel = v;
11785                 return;
11786             }
11787             
11788             Roo.log('Cannot Found any of label > span || label in input');
11789             return;
11790         }
11791         
11792         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11793         this.fieldLabel = v;
11794         
11795         
11796     }
11797 });
11798
11799  
11800 /*
11801  * - LGPL
11802  *
11803  * Input
11804  * 
11805  */
11806
11807 /**
11808  * @class Roo.bootstrap.TextArea
11809  * @extends Roo.bootstrap.Input
11810  * Bootstrap TextArea class
11811  * @cfg {Number} cols Specifies the visible width of a text area
11812  * @cfg {Number} rows Specifies the visible number of lines in a text area
11813  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11814  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11815  * @cfg {string} html text
11816  * 
11817  * @constructor
11818  * Create a new TextArea
11819  * @param {Object} config The config object
11820  */
11821
11822 Roo.bootstrap.TextArea = function(config){
11823     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11824    
11825 };
11826
11827 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11828      
11829     cols : false,
11830     rows : 5,
11831     readOnly : false,
11832     warp : 'soft',
11833     resize : false,
11834     value: false,
11835     html: false,
11836     
11837     getAutoCreate : function(){
11838         
11839         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11840         
11841         var id = Roo.id();
11842         
11843         var cfg = {};
11844         
11845         if(this.inputType != 'hidden'){
11846             cfg.cls = 'form-group' //input-group
11847         }
11848         
11849         var input =  {
11850             tag: 'textarea',
11851             id : id,
11852             warp : this.warp,
11853             rows : this.rows,
11854             value : this.value || '',
11855             html: this.html || '',
11856             cls : 'form-control',
11857             placeholder : this.placeholder || '' 
11858             
11859         };
11860         
11861         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11862             input.maxLength = this.maxLength;
11863         }
11864         
11865         if(this.resize){
11866             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11867         }
11868         
11869         if(this.cols){
11870             input.cols = this.cols;
11871         }
11872         
11873         if (this.readOnly) {
11874             input.readonly = true;
11875         }
11876         
11877         if (this.name) {
11878             input.name = this.name;
11879         }
11880         
11881         if (this.size) {
11882             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11883         }
11884         
11885         var settings=this;
11886         ['xs','sm','md','lg'].map(function(size){
11887             if (settings[size]) {
11888                 cfg.cls += ' col-' + size + '-' + settings[size];
11889             }
11890         });
11891         
11892         var inputblock = input;
11893         
11894         if(this.hasFeedback && !this.allowBlank){
11895             
11896             var feedback = {
11897                 tag: 'span',
11898                 cls: 'glyphicon form-control-feedback'
11899             };
11900
11901             inputblock = {
11902                 cls : 'has-feedback',
11903                 cn :  [
11904                     input,
11905                     feedback
11906                 ] 
11907             };  
11908         }
11909         
11910         
11911         if (this.before || this.after) {
11912             
11913             inputblock = {
11914                 cls : 'input-group',
11915                 cn :  [] 
11916             };
11917             if (this.before) {
11918                 inputblock.cn.push({
11919                     tag :'span',
11920                     cls : 'input-group-addon',
11921                     html : this.before
11922                 });
11923             }
11924             
11925             inputblock.cn.push(input);
11926             
11927             if(this.hasFeedback && !this.allowBlank){
11928                 inputblock.cls += ' has-feedback';
11929                 inputblock.cn.push(feedback);
11930             }
11931             
11932             if (this.after) {
11933                 inputblock.cn.push({
11934                     tag :'span',
11935                     cls : 'input-group-addon',
11936                     html : this.after
11937                 });
11938             }
11939             
11940         }
11941         
11942         if (align ==='left' && this.fieldLabel.length) {
11943             cfg.cn = [
11944                 {
11945                     tag: 'label',
11946                     'for' :  id,
11947                     cls : 'control-label',
11948                     html : this.fieldLabel
11949                 },
11950                 {
11951                     cls : "",
11952                     cn: [
11953                         inputblock
11954                     ]
11955                 }
11956
11957             ];
11958             
11959             if(this.labelWidth > 12){
11960                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11961             }
11962
11963             if(this.labelWidth < 13 && this.labelmd == 0){
11964                 this.labelmd = this.labelWidth;
11965             }
11966
11967             if(this.labellg > 0){
11968                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11969                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11970             }
11971
11972             if(this.labelmd > 0){
11973                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11974                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11975             }
11976
11977             if(this.labelsm > 0){
11978                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11979                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11980             }
11981
11982             if(this.labelxs > 0){
11983                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11984                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11985             }
11986             
11987         } else if ( this.fieldLabel.length) {
11988             cfg.cn = [
11989
11990                {
11991                    tag: 'label',
11992                    //cls : 'input-group-addon',
11993                    html : this.fieldLabel
11994
11995                },
11996
11997                inputblock
11998
11999            ];
12000
12001         } else {
12002
12003             cfg.cn = [
12004
12005                 inputblock
12006
12007             ];
12008                 
12009         }
12010         
12011         if (this.disabled) {
12012             input.disabled=true;
12013         }
12014         
12015         return cfg;
12016         
12017     },
12018     /**
12019      * return the real textarea element.
12020      */
12021     inputEl: function ()
12022     {
12023         return this.el.select('textarea.form-control',true).first();
12024     },
12025     
12026     /**
12027      * Clear any invalid styles/messages for this field
12028      */
12029     clearInvalid : function()
12030     {
12031         
12032         if(!this.el || this.preventMark){ // not rendered
12033             return;
12034         }
12035         
12036         var label = this.el.select('label', true).first();
12037         var icon = this.el.select('i.fa-star', true).first();
12038         
12039         if(label && icon){
12040             icon.remove();
12041         }
12042         this.el.removeClass( this.validClass);
12043         this.inputEl().removeClass('is-invalid');
12044          
12045         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12046             
12047             var feedback = this.el.select('.form-control-feedback', true).first();
12048             
12049             if(feedback){
12050                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12051             }
12052             
12053         }
12054         
12055         this.fireEvent('valid', this);
12056     },
12057     
12058      /**
12059      * Mark this field as valid
12060      */
12061     markValid : function()
12062     {
12063         if(!this.el  || this.preventMark){ // not rendered
12064             return;
12065         }
12066         
12067         this.el.removeClass([this.invalidClass, this.validClass]);
12068         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12069         
12070         var feedback = this.el.select('.form-control-feedback', true).first();
12071             
12072         if(feedback){
12073             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12074         }
12075
12076         if(this.disabled || this.allowBlank){
12077             return;
12078         }
12079         
12080         var label = this.el.select('label', true).first();
12081         var icon = this.el.select('i.fa-star', true).first();
12082         
12083         if(label && icon){
12084             icon.remove();
12085         }
12086         if (Roo.bootstrap.version == 3) {
12087             this.el.addClass(this.validClass);
12088         } else {
12089             this.inputEl().addClass('is-valid');
12090         }
12091         
12092         
12093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12094             
12095             var feedback = this.el.select('.form-control-feedback', true).first();
12096             
12097             if(feedback){
12098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12100             }
12101             
12102         }
12103         
12104         this.fireEvent('valid', this);
12105     },
12106     
12107      /**
12108      * Mark this field as invalid
12109      * @param {String} msg The validation message
12110      */
12111     markInvalid : function(msg)
12112     {
12113         if(!this.el  || this.preventMark){ // not rendered
12114             return;
12115         }
12116         
12117         this.el.removeClass([this.invalidClass, this.validClass]);
12118         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12119         
12120         var feedback = this.el.select('.form-control-feedback', true).first();
12121             
12122         if(feedback){
12123             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12124         }
12125
12126         if(this.disabled || this.allowBlank){
12127             return;
12128         }
12129         
12130         var label = this.el.select('label', true).first();
12131         var icon = this.el.select('i.fa-star', true).first();
12132         
12133         if(!this.getValue().length && label && !icon){
12134             this.el.createChild({
12135                 tag : 'i',
12136                 cls : 'text-danger fa fa-lg fa-star',
12137                 tooltip : 'This field is required',
12138                 style : 'margin-right:5px;'
12139             }, label, true);
12140         }
12141         
12142         if (Roo.bootstrap.version == 3) {
12143             this.el.addClass(this.invalidClass);
12144         } else {
12145             this.inputEl().addClass('is-invalid');
12146         }
12147         
12148         // fixme ... this may be depricated need to test..
12149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12150             
12151             var feedback = this.el.select('.form-control-feedback', true).first();
12152             
12153             if(feedback){
12154                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12155                 
12156                 if(this.getValue().length || this.forceFeedback){
12157                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12158                 }
12159                 
12160             }
12161             
12162         }
12163         
12164         this.fireEvent('invalid', this, msg);
12165     }
12166 });
12167
12168  
12169 /*
12170  * - LGPL
12171  *
12172  * trigger field - base class for combo..
12173  * 
12174  */
12175  
12176 /**
12177  * @class Roo.bootstrap.TriggerField
12178  * @extends Roo.bootstrap.Input
12179  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12180  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12181  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12182  * for which you can provide a custom implementation.  For example:
12183  * <pre><code>
12184 var trigger = new Roo.bootstrap.TriggerField();
12185 trigger.onTriggerClick = myTriggerFn;
12186 trigger.applyTo('my-field');
12187 </code></pre>
12188  *
12189  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12190  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12191  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12192  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12193  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12194
12195  * @constructor
12196  * Create a new TriggerField.
12197  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12198  * to the base TextField)
12199  */
12200 Roo.bootstrap.TriggerField = function(config){
12201     this.mimicing = false;
12202     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12203 };
12204
12205 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12206     /**
12207      * @cfg {String} triggerClass A CSS class to apply to the trigger
12208      */
12209      /**
12210      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12211      */
12212     hideTrigger:false,
12213
12214     /**
12215      * @cfg {Boolean} removable (true|false) special filter default false
12216      */
12217     removable : false,
12218     
12219     /** @cfg {Boolean} grow @hide */
12220     /** @cfg {Number} growMin @hide */
12221     /** @cfg {Number} growMax @hide */
12222
12223     /**
12224      * @hide 
12225      * @method
12226      */
12227     autoSize: Roo.emptyFn,
12228     // private
12229     monitorTab : true,
12230     // private
12231     deferHeight : true,
12232
12233     
12234     actionMode : 'wrap',
12235     
12236     caret : false,
12237     
12238     
12239     getAutoCreate : function(){
12240        
12241         var align = this.labelAlign || this.parentLabelAlign();
12242         
12243         var id = Roo.id();
12244         
12245         var cfg = {
12246             cls: 'form-group' //input-group
12247         };
12248         
12249         
12250         var input =  {
12251             tag: 'input',
12252             id : id,
12253             type : this.inputType,
12254             cls : 'form-control',
12255             autocomplete: 'new-password',
12256             placeholder : this.placeholder || '' 
12257             
12258         };
12259         if (this.name) {
12260             input.name = this.name;
12261         }
12262         if (this.size) {
12263             input.cls += ' input-' + this.size;
12264         }
12265         
12266         if (this.disabled) {
12267             input.disabled=true;
12268         }
12269         
12270         var inputblock = input;
12271         
12272         if(this.hasFeedback && !this.allowBlank){
12273             
12274             var feedback = {
12275                 tag: 'span',
12276                 cls: 'glyphicon form-control-feedback'
12277             };
12278             
12279             if(this.removable && !this.editable  ){
12280                 inputblock = {
12281                     cls : 'has-feedback',
12282                     cn :  [
12283                         inputblock,
12284                         {
12285                             tag: 'button',
12286                             html : 'x',
12287                             cls : 'roo-combo-removable-btn close'
12288                         },
12289                         feedback
12290                     ] 
12291                 };
12292             } else {
12293                 inputblock = {
12294                     cls : 'has-feedback',
12295                     cn :  [
12296                         inputblock,
12297                         feedback
12298                     ] 
12299                 };
12300             }
12301
12302         } else {
12303             if(this.removable && !this.editable ){
12304                 inputblock = {
12305                     cls : 'roo-removable',
12306                     cn :  [
12307                         inputblock,
12308                         {
12309                             tag: 'button',
12310                             html : 'x',
12311                             cls : 'roo-combo-removable-btn close'
12312                         }
12313                     ] 
12314                 };
12315             }
12316         }
12317         
12318         if (this.before || this.after) {
12319             
12320             inputblock = {
12321                 cls : 'input-group',
12322                 cn :  [] 
12323             };
12324             if (this.before) {
12325                 inputblock.cn.push({
12326                     tag :'span',
12327                     cls : 'input-group-addon input-group-prepend input-group-text',
12328                     html : this.before
12329                 });
12330             }
12331             
12332             inputblock.cn.push(input);
12333             
12334             if(this.hasFeedback && !this.allowBlank){
12335                 inputblock.cls += ' has-feedback';
12336                 inputblock.cn.push(feedback);
12337             }
12338             
12339             if (this.after) {
12340                 inputblock.cn.push({
12341                     tag :'span',
12342                     cls : 'input-group-addon input-group-append input-group-text',
12343                     html : this.after
12344                 });
12345             }
12346             
12347         };
12348         
12349       
12350         
12351         var ibwrap = inputblock;
12352         
12353         if(this.multiple){
12354             ibwrap = {
12355                 tag: 'ul',
12356                 cls: 'roo-select2-choices',
12357                 cn:[
12358                     {
12359                         tag: 'li',
12360                         cls: 'roo-select2-search-field',
12361                         cn: [
12362
12363                             inputblock
12364                         ]
12365                     }
12366                 ]
12367             };
12368                 
12369         }
12370         
12371         var combobox = {
12372             cls: 'roo-select2-container input-group',
12373             cn: [
12374                  {
12375                     tag: 'input',
12376                     type : 'hidden',
12377                     cls: 'form-hidden-field'
12378                 },
12379                 ibwrap
12380             ]
12381         };
12382         
12383         if(!this.multiple && this.showToggleBtn){
12384             
12385             var caret = {
12386                         tag: 'span',
12387                         cls: 'caret'
12388              };
12389             if (this.caret != false) {
12390                 caret = {
12391                      tag: 'i',
12392                      cls: 'fa fa-' + this.caret
12393                 };
12394                 
12395             }
12396             
12397             combobox.cn.push({
12398                 tag :'span',
12399                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12400                 cn : [
12401                     Roo.bootstrap.version == 3 ? caret : '',
12402                     {
12403                         tag: 'span',
12404                         cls: 'combobox-clear',
12405                         cn  : [
12406                             {
12407                                 tag : 'i',
12408                                 cls: 'icon-remove'
12409                             }
12410                         ]
12411                     }
12412                 ]
12413
12414             })
12415         }
12416         
12417         if(this.multiple){
12418             combobox.cls += ' roo-select2-container-multi';
12419         }
12420          var indicator = {
12421             tag : 'i',
12422             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12423             tooltip : 'This field is required'
12424         };
12425         if (Roo.bootstrap.version == 4) {
12426             indicator = {
12427                 tag : 'i',
12428                 style : 'display:none'
12429             };
12430         }
12431         
12432         
12433         if (align ==='left' && this.fieldLabel.length) {
12434             
12435             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12436
12437             cfg.cn = [
12438                 indicator,
12439                 {
12440                     tag: 'label',
12441                     'for' :  id,
12442                     cls : 'control-label',
12443                     html : this.fieldLabel
12444
12445                 },
12446                 {
12447                     cls : "", 
12448                     cn: [
12449                         combobox
12450                     ]
12451                 }
12452
12453             ];
12454             
12455             var labelCfg = cfg.cn[1];
12456             var contentCfg = cfg.cn[2];
12457             
12458             if(this.indicatorpos == 'right'){
12459                 cfg.cn = [
12460                     {
12461                         tag: 'label',
12462                         'for' :  id,
12463                         cls : 'control-label',
12464                         cn : [
12465                             {
12466                                 tag : 'span',
12467                                 html : this.fieldLabel
12468                             },
12469                             indicator
12470                         ]
12471                     },
12472                     {
12473                         cls : "", 
12474                         cn: [
12475                             combobox
12476                         ]
12477                     }
12478
12479                 ];
12480                 
12481                 labelCfg = cfg.cn[0];
12482                 contentCfg = cfg.cn[1];
12483             }
12484             
12485             if(this.labelWidth > 12){
12486                 labelCfg.style = "width: " + this.labelWidth + 'px';
12487             }
12488             
12489             if(this.labelWidth < 13 && this.labelmd == 0){
12490                 this.labelmd = this.labelWidth;
12491             }
12492             
12493             if(this.labellg > 0){
12494                 labelCfg.cls += ' col-lg-' + this.labellg;
12495                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12496             }
12497             
12498             if(this.labelmd > 0){
12499                 labelCfg.cls += ' col-md-' + this.labelmd;
12500                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12501             }
12502             
12503             if(this.labelsm > 0){
12504                 labelCfg.cls += ' col-sm-' + this.labelsm;
12505                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12506             }
12507             
12508             if(this.labelxs > 0){
12509                 labelCfg.cls += ' col-xs-' + this.labelxs;
12510                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12511             }
12512             
12513         } else if ( this.fieldLabel.length) {
12514 //                Roo.log(" label");
12515             cfg.cn = [
12516                 indicator,
12517                {
12518                    tag: 'label',
12519                    //cls : 'input-group-addon',
12520                    html : this.fieldLabel
12521
12522                },
12523
12524                combobox
12525
12526             ];
12527             
12528             if(this.indicatorpos == 'right'){
12529                 
12530                 cfg.cn = [
12531                     {
12532                        tag: 'label',
12533                        cn : [
12534                            {
12535                                tag : 'span',
12536                                html : this.fieldLabel
12537                            },
12538                            indicator
12539                        ]
12540
12541                     },
12542                     combobox
12543
12544                 ];
12545
12546             }
12547
12548         } else {
12549             
12550 //                Roo.log(" no label && no align");
12551                 cfg = combobox
12552                      
12553                 
12554         }
12555         
12556         var settings=this;
12557         ['xs','sm','md','lg'].map(function(size){
12558             if (settings[size]) {
12559                 cfg.cls += ' col-' + size + '-' + settings[size];
12560             }
12561         });
12562         
12563         return cfg;
12564         
12565     },
12566     
12567     
12568     
12569     // private
12570     onResize : function(w, h){
12571 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12572 //        if(typeof w == 'number'){
12573 //            var x = w - this.trigger.getWidth();
12574 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12575 //            this.trigger.setStyle('left', x+'px');
12576 //        }
12577     },
12578
12579     // private
12580     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12581
12582     // private
12583     getResizeEl : function(){
12584         return this.inputEl();
12585     },
12586
12587     // private
12588     getPositionEl : function(){
12589         return this.inputEl();
12590     },
12591
12592     // private
12593     alignErrorIcon : function(){
12594         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12595     },
12596
12597     // private
12598     initEvents : function(){
12599         
12600         this.createList();
12601         
12602         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12603         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12604         if(!this.multiple && this.showToggleBtn){
12605             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12606             if(this.hideTrigger){
12607                 this.trigger.setDisplayed(false);
12608             }
12609             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12610         }
12611         
12612         if(this.multiple){
12613             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12614         }
12615         
12616         if(this.removable && !this.editable && !this.tickable){
12617             var close = this.closeTriggerEl();
12618             
12619             if(close){
12620                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12621                 close.on('click', this.removeBtnClick, this, close);
12622             }
12623         }
12624         
12625         //this.trigger.addClassOnOver('x-form-trigger-over');
12626         //this.trigger.addClassOnClick('x-form-trigger-click');
12627         
12628         //if(!this.width){
12629         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12630         //}
12631     },
12632     
12633     closeTriggerEl : function()
12634     {
12635         var close = this.el.select('.roo-combo-removable-btn', true).first();
12636         return close ? close : false;
12637     },
12638     
12639     removeBtnClick : function(e, h, el)
12640     {
12641         e.preventDefault();
12642         
12643         if(this.fireEvent("remove", this) !== false){
12644             this.reset();
12645             this.fireEvent("afterremove", this)
12646         }
12647     },
12648     
12649     createList : function()
12650     {
12651         this.list = Roo.get(document.body).createChild({
12652             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12653             cls: 'typeahead typeahead-long dropdown-menu shadow',
12654             style: 'display:none'
12655         });
12656         
12657         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12658         
12659     },
12660
12661     // private
12662     initTrigger : function(){
12663        
12664     },
12665
12666     // private
12667     onDestroy : function(){
12668         if(this.trigger){
12669             this.trigger.removeAllListeners();
12670           //  this.trigger.remove();
12671         }
12672         //if(this.wrap){
12673         //    this.wrap.remove();
12674         //}
12675         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12676     },
12677
12678     // private
12679     onFocus : function(){
12680         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12681         /*
12682         if(!this.mimicing){
12683             this.wrap.addClass('x-trigger-wrap-focus');
12684             this.mimicing = true;
12685             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12686             if(this.monitorTab){
12687                 this.el.on("keydown", this.checkTab, this);
12688             }
12689         }
12690         */
12691     },
12692
12693     // private
12694     checkTab : function(e){
12695         if(e.getKey() == e.TAB){
12696             this.triggerBlur();
12697         }
12698     },
12699
12700     // private
12701     onBlur : function(){
12702         // do nothing
12703     },
12704
12705     // private
12706     mimicBlur : function(e, t){
12707         /*
12708         if(!this.wrap.contains(t) && this.validateBlur()){
12709             this.triggerBlur();
12710         }
12711         */
12712     },
12713
12714     // private
12715     triggerBlur : function(){
12716         this.mimicing = false;
12717         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12718         if(this.monitorTab){
12719             this.el.un("keydown", this.checkTab, this);
12720         }
12721         //this.wrap.removeClass('x-trigger-wrap-focus');
12722         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12723     },
12724
12725     // private
12726     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12727     validateBlur : function(e, t){
12728         return true;
12729     },
12730
12731     // private
12732     onDisable : function(){
12733         this.inputEl().dom.disabled = true;
12734         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12735         //if(this.wrap){
12736         //    this.wrap.addClass('x-item-disabled');
12737         //}
12738     },
12739
12740     // private
12741     onEnable : function(){
12742         this.inputEl().dom.disabled = false;
12743         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12744         //if(this.wrap){
12745         //    this.el.removeClass('x-item-disabled');
12746         //}
12747     },
12748
12749     // private
12750     onShow : function(){
12751         var ae = this.getActionEl();
12752         
12753         if(ae){
12754             ae.dom.style.display = '';
12755             ae.dom.style.visibility = 'visible';
12756         }
12757     },
12758
12759     // private
12760     
12761     onHide : function(){
12762         var ae = this.getActionEl();
12763         ae.dom.style.display = 'none';
12764     },
12765
12766     /**
12767      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12768      * by an implementing function.
12769      * @method
12770      * @param {EventObject} e
12771      */
12772     onTriggerClick : Roo.emptyFn
12773 });
12774  
12775 /*
12776 * Licence: LGPL
12777 */
12778
12779 /**
12780  * @class Roo.bootstrap.CardUploader
12781  * @extends Roo.bootstrap.Button
12782  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12783  * @cfg {Number} errorTimeout default 3000
12784  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12785  * @cfg {Array}  html The button text.
12786
12787  *
12788  * @constructor
12789  * Create a new CardUploader
12790  * @param {Object} config The config object
12791  */
12792
12793 Roo.bootstrap.CardUploader = function(config){
12794     
12795  
12796     
12797     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12798     
12799     
12800     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12801         return r.data.id
12802      });
12803     
12804      this.addEvents({
12805          // raw events
12806         /**
12807          * @event preview
12808          * When a image is clicked on - and needs to display a slideshow or similar..
12809          * @param {Roo.bootstrap.Card} this
12810          * @param {Object} The image information data 
12811          *
12812          */
12813         'preview' : true,
12814          /**
12815          * @event download
12816          * When a the download link is clicked
12817          * @param {Roo.bootstrap.Card} this
12818          * @param {Object} The image information data  contains 
12819          */
12820         'download' : true
12821         
12822     });
12823 };
12824  
12825 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12826     
12827      
12828     errorTimeout : 3000,
12829      
12830     images : false,
12831    
12832     fileCollection : false,
12833     allowBlank : true,
12834     
12835     getAutoCreate : function()
12836     {
12837         
12838         var cfg =  {
12839             cls :'form-group' ,
12840             cn : [
12841                
12842                 {
12843                     tag: 'label',
12844                    //cls : 'input-group-addon',
12845                     html : this.fieldLabel
12846
12847                 },
12848
12849                 {
12850                     tag: 'input',
12851                     type : 'hidden',
12852                     name : this.name,
12853                     value : this.value,
12854                     cls : 'd-none  form-control'
12855                 },
12856                 
12857                 {
12858                     tag: 'input',
12859                     multiple : 'multiple',
12860                     type : 'file',
12861                     cls : 'd-none  roo-card-upload-selector'
12862                 },
12863                 
12864                 {
12865                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12866                 },
12867                 {
12868                     cls : 'card-columns roo-card-uploader-container'
12869                 }
12870
12871             ]
12872         };
12873            
12874          
12875         return cfg;
12876     },
12877     
12878     getChildContainer : function() /// what children are added to.
12879     {
12880         return this.containerEl;
12881     },
12882    
12883     getButtonContainer : function() /// what children are added to.
12884     {
12885         return this.el.select(".roo-card-uploader-button-container").first();
12886     },
12887    
12888     initEvents : function()
12889     {
12890         
12891         Roo.bootstrap.Input.prototype.initEvents.call(this);
12892         
12893         var t = this;
12894         this.addxtype({
12895             xns: Roo.bootstrap,
12896
12897             xtype : 'Button',
12898             container_method : 'getButtonContainer' ,            
12899             html :  this.html, // fix changable?
12900             cls : 'w-100 ',
12901             listeners : {
12902                 'click' : function(btn, e) {
12903                     t.onClick(e);
12904                 }
12905             }
12906         });
12907         
12908         
12909         
12910         
12911         this.urlAPI = (window.createObjectURL && window) || 
12912                                 (window.URL && URL.revokeObjectURL && URL) || 
12913                                 (window.webkitURL && webkitURL);
12914                         
12915          
12916          
12917          
12918         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12919         
12920         this.selectorEl.on('change', this.onFileSelected, this);
12921         if (this.images) {
12922             var t = this;
12923             this.images.forEach(function(img) {
12924                 t.addCard(img)
12925             });
12926             this.images = false;
12927         }
12928         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12929          
12930        
12931     },
12932     
12933    
12934     onClick : function(e)
12935     {
12936         e.preventDefault();
12937          
12938         this.selectorEl.dom.click();
12939          
12940     },
12941     
12942     onFileSelected : function(e)
12943     {
12944         e.preventDefault();
12945         
12946         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12947             return;
12948         }
12949         
12950         Roo.each(this.selectorEl.dom.files, function(file){    
12951             this.addFile(file);
12952         }, this);
12953          
12954     },
12955     
12956       
12957     
12958       
12959     
12960     addFile : function(file)
12961     {
12962            
12963         if(typeof(file) === 'string'){
12964             throw "Add file by name?"; // should not happen
12965             return;
12966         }
12967         
12968         if(!file || !this.urlAPI){
12969             return;
12970         }
12971         
12972         // file;
12973         // file.type;
12974         
12975         var _this = this;
12976         
12977         
12978         var url = _this.urlAPI.createObjectURL( file);
12979            
12980         this.addCard({
12981             id : Roo.bootstrap.CardUploader.ID--,
12982             is_uploaded : false,
12983             src : url,
12984             srcfile : file,
12985             title : file.name,
12986             mimetype : file.type,
12987             preview : false,
12988             is_deleted : 0
12989         });
12990         
12991     },
12992     
12993     /**
12994      * addCard - add an Attachment to the uploader
12995      * @param data - the data about the image to upload
12996      *
12997      * {
12998           id : 123
12999           title : "Title of file",
13000           is_uploaded : false,
13001           src : "http://.....",
13002           srcfile : { the File upload object },
13003           mimetype : file.type,
13004           preview : false,
13005           is_deleted : 0
13006           .. any other data...
13007         }
13008      *
13009      * 
13010     */
13011     
13012     addCard : function (data)
13013     {
13014         // hidden input element?
13015         // if the file is not an image...
13016         //then we need to use something other that and header_image
13017         var t = this;
13018         //   remove.....
13019         var footer = [
13020             {
13021                 xns : Roo.bootstrap,
13022                 xtype : 'CardFooter',
13023                  items: [
13024                     {
13025                         xns : Roo.bootstrap,
13026                         xtype : 'Element',
13027                         cls : 'd-flex',
13028                         items : [
13029                             
13030                             {
13031                                 xns : Roo.bootstrap,
13032                                 xtype : 'Button',
13033                                 html : String.format("<small>{0}</small>", data.title),
13034                                 cls : 'col-10 text-left',
13035                                 size: 'sm',
13036                                 weight: 'link',
13037                                 fa : 'download',
13038                                 listeners : {
13039                                     click : function() {
13040                                      
13041                                         t.fireEvent( "download", t, data );
13042                                     }
13043                                 }
13044                             },
13045                           
13046                             {
13047                                 xns : Roo.bootstrap,
13048                                 xtype : 'Button',
13049                                 style: 'max-height: 28px; ',
13050                                 size : 'sm',
13051                                 weight: 'danger',
13052                                 cls : 'col-2',
13053                                 fa : 'times',
13054                                 listeners : {
13055                                     click : function() {
13056                                         t.removeCard(data.id)
13057                                     }
13058                                 }
13059                             }
13060                         ]
13061                     }
13062                     
13063                 ] 
13064             }
13065             
13066         ];
13067         
13068         var cn = this.addxtype(
13069             {
13070                  
13071                 xns : Roo.bootstrap,
13072                 xtype : 'Card',
13073                 closeable : true,
13074                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13075                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13076                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13077                 data : data,
13078                 html : false,
13079                  
13080                 items : footer,
13081                 initEvents : function() {
13082                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13083                     var card = this;
13084                     this.imgEl = this.el.select('.card-img-top').first();
13085                     if (this.imgEl) {
13086                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13087                         this.imgEl.set({ 'pointer' : 'cursor' });
13088                                   
13089                     }
13090                     this.getCardFooter().addClass('p-1');
13091                     
13092                   
13093                 }
13094                 
13095             }
13096         );
13097         // dont' really need ot update items.
13098         // this.items.push(cn);
13099         this.fileCollection.add(cn);
13100         
13101         if (!data.srcfile) {
13102             this.updateInput();
13103             return;
13104         }
13105             
13106         var _t = this;
13107         var reader = new FileReader();
13108         reader.addEventListener("load", function() {  
13109             data.srcdata =  reader.result;
13110             _t.updateInput();
13111         });
13112         reader.readAsDataURL(data.srcfile);
13113         
13114         
13115         
13116     },
13117     removeCard : function(id)
13118     {
13119         
13120         var card  = this.fileCollection.get(id);
13121         card.data.is_deleted = 1;
13122         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13123         //this.fileCollection.remove(card);
13124         //this.items = this.items.filter(function(e) { return e != card });
13125         // dont' really need ot update items.
13126         card.el.dom.parentNode.removeChild(card.el.dom);
13127         this.updateInput();
13128
13129         
13130     },
13131     reset: function()
13132     {
13133         this.fileCollection.each(function(card) {
13134             if (card.el.dom && card.el.dom.parentNode) {
13135                 card.el.dom.parentNode.removeChild(card.el.dom);
13136             }
13137         });
13138         this.fileCollection.clear();
13139         this.updateInput();
13140     },
13141     
13142     updateInput : function()
13143     {
13144          var data = [];
13145         this.fileCollection.each(function(e) {
13146             data.push(e.data);
13147             
13148         });
13149         this.inputEl().dom.value = JSON.stringify(data);
13150         
13151         
13152         
13153     }
13154     
13155     
13156 });
13157
13158
13159 Roo.bootstrap.CardUploader.ID = -1;/*
13160  * Based on:
13161  * Ext JS Library 1.1.1
13162  * Copyright(c) 2006-2007, Ext JS, LLC.
13163  *
13164  * Originally Released Under LGPL - original licence link has changed is not relivant.
13165  *
13166  * Fork - LGPL
13167  * <script type="text/javascript">
13168  */
13169
13170
13171 /**
13172  * @class Roo.data.SortTypes
13173  * @singleton
13174  * Defines the default sorting (casting?) comparison functions used when sorting data.
13175  */
13176 Roo.data.SortTypes = {
13177     /**
13178      * Default sort that does nothing
13179      * @param {Mixed} s The value being converted
13180      * @return {Mixed} The comparison value
13181      */
13182     none : function(s){
13183         return s;
13184     },
13185     
13186     /**
13187      * The regular expression used to strip tags
13188      * @type {RegExp}
13189      * @property
13190      */
13191     stripTagsRE : /<\/?[^>]+>/gi,
13192     
13193     /**
13194      * Strips all HTML tags to sort on text only
13195      * @param {Mixed} s The value being converted
13196      * @return {String} The comparison value
13197      */
13198     asText : function(s){
13199         return String(s).replace(this.stripTagsRE, "");
13200     },
13201     
13202     /**
13203      * Strips all HTML tags to sort on text only - Case insensitive
13204      * @param {Mixed} s The value being converted
13205      * @return {String} The comparison value
13206      */
13207     asUCText : function(s){
13208         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13209     },
13210     
13211     /**
13212      * Case insensitive string
13213      * @param {Mixed} s The value being converted
13214      * @return {String} The comparison value
13215      */
13216     asUCString : function(s) {
13217         return String(s).toUpperCase();
13218     },
13219     
13220     /**
13221      * Date sorting
13222      * @param {Mixed} s The value being converted
13223      * @return {Number} The comparison value
13224      */
13225     asDate : function(s) {
13226         if(!s){
13227             return 0;
13228         }
13229         if(s instanceof Date){
13230             return s.getTime();
13231         }
13232         return Date.parse(String(s));
13233     },
13234     
13235     /**
13236      * Float sorting
13237      * @param {Mixed} s The value being converted
13238      * @return {Float} The comparison value
13239      */
13240     asFloat : function(s) {
13241         var val = parseFloat(String(s).replace(/,/g, ""));
13242         if(isNaN(val)) {
13243             val = 0;
13244         }
13245         return val;
13246     },
13247     
13248     /**
13249      * Integer sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Number} The comparison value
13252      */
13253     asInt : function(s) {
13254         var val = parseInt(String(s).replace(/,/g, ""));
13255         if(isNaN(val)) {
13256             val = 0;
13257         }
13258         return val;
13259     }
13260 };/*
13261  * Based on:
13262  * Ext JS Library 1.1.1
13263  * Copyright(c) 2006-2007, Ext JS, LLC.
13264  *
13265  * Originally Released Under LGPL - original licence link has changed is not relivant.
13266  *
13267  * Fork - LGPL
13268  * <script type="text/javascript">
13269  */
13270
13271 /**
13272 * @class Roo.data.Record
13273  * Instances of this class encapsulate both record <em>definition</em> information, and record
13274  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13275  * to access Records cached in an {@link Roo.data.Store} object.<br>
13276  * <p>
13277  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13278  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13279  * objects.<br>
13280  * <p>
13281  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13282  * @constructor
13283  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13284  * {@link #create}. The parameters are the same.
13285  * @param {Array} data An associative Array of data values keyed by the field name.
13286  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13287  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13288  * not specified an integer id is generated.
13289  */
13290 Roo.data.Record = function(data, id){
13291     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13292     this.data = data;
13293 };
13294
13295 /**
13296  * Generate a constructor for a specific record layout.
13297  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13298  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13299  * Each field definition object may contain the following properties: <ul>
13300  * <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,
13301  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13302  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13303  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13304  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13305  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13306  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13307  * this may be omitted.</p></li>
13308  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13309  * <ul><li>auto (Default, implies no conversion)</li>
13310  * <li>string</li>
13311  * <li>int</li>
13312  * <li>float</li>
13313  * <li>boolean</li>
13314  * <li>date</li></ul></p></li>
13315  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13316  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13317  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13318  * by the Reader into an object that will be stored in the Record. It is passed the
13319  * following parameters:<ul>
13320  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13321  * </ul></p></li>
13322  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13323  * </ul>
13324  * <br>usage:<br><pre><code>
13325 var TopicRecord = Roo.data.Record.create(
13326     {name: 'title', mapping: 'topic_title'},
13327     {name: 'author', mapping: 'username'},
13328     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13329     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13330     {name: 'lastPoster', mapping: 'user2'},
13331     {name: 'excerpt', mapping: 'post_text'}
13332 );
13333
13334 var myNewRecord = new TopicRecord({
13335     title: 'Do my job please',
13336     author: 'noobie',
13337     totalPosts: 1,
13338     lastPost: new Date(),
13339     lastPoster: 'Animal',
13340     excerpt: 'No way dude!'
13341 });
13342 myStore.add(myNewRecord);
13343 </code></pre>
13344  * @method create
13345  * @static
13346  */
13347 Roo.data.Record.create = function(o){
13348     var f = function(){
13349         f.superclass.constructor.apply(this, arguments);
13350     };
13351     Roo.extend(f, Roo.data.Record);
13352     var p = f.prototype;
13353     p.fields = new Roo.util.MixedCollection(false, function(field){
13354         return field.name;
13355     });
13356     for(var i = 0, len = o.length; i < len; i++){
13357         p.fields.add(new Roo.data.Field(o[i]));
13358     }
13359     f.getField = function(name){
13360         return p.fields.get(name);  
13361     };
13362     return f;
13363 };
13364
13365 Roo.data.Record.AUTO_ID = 1000;
13366 Roo.data.Record.EDIT = 'edit';
13367 Roo.data.Record.REJECT = 'reject';
13368 Roo.data.Record.COMMIT = 'commit';
13369
13370 Roo.data.Record.prototype = {
13371     /**
13372      * Readonly flag - true if this record has been modified.
13373      * @type Boolean
13374      */
13375     dirty : false,
13376     editing : false,
13377     error: null,
13378     modified: null,
13379
13380     // private
13381     join : function(store){
13382         this.store = store;
13383     },
13384
13385     /**
13386      * Set the named field to the specified value.
13387      * @param {String} name The name of the field to set.
13388      * @param {Object} value The value to set the field to.
13389      */
13390     set : function(name, value){
13391         if(this.data[name] == value){
13392             return;
13393         }
13394         this.dirty = true;
13395         if(!this.modified){
13396             this.modified = {};
13397         }
13398         if(typeof this.modified[name] == 'undefined'){
13399             this.modified[name] = this.data[name];
13400         }
13401         this.data[name] = value;
13402         if(!this.editing && this.store){
13403             this.store.afterEdit(this);
13404         }       
13405     },
13406
13407     /**
13408      * Get the value of the named field.
13409      * @param {String} name The name of the field to get the value of.
13410      * @return {Object} The value of the field.
13411      */
13412     get : function(name){
13413         return this.data[name]; 
13414     },
13415
13416     // private
13417     beginEdit : function(){
13418         this.editing = true;
13419         this.modified = {}; 
13420     },
13421
13422     // private
13423     cancelEdit : function(){
13424         this.editing = false;
13425         delete this.modified;
13426     },
13427
13428     // private
13429     endEdit : function(){
13430         this.editing = false;
13431         if(this.dirty && this.store){
13432             this.store.afterEdit(this);
13433         }
13434     },
13435
13436     /**
13437      * Usually called by the {@link Roo.data.Store} which owns the Record.
13438      * Rejects all changes made to the Record since either creation, or the last commit operation.
13439      * Modified fields are reverted to their original values.
13440      * <p>
13441      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13442      * of reject operations.
13443      */
13444     reject : function(){
13445         var m = this.modified;
13446         for(var n in m){
13447             if(typeof m[n] != "function"){
13448                 this.data[n] = m[n];
13449             }
13450         }
13451         this.dirty = false;
13452         delete this.modified;
13453         this.editing = false;
13454         if(this.store){
13455             this.store.afterReject(this);
13456         }
13457     },
13458
13459     /**
13460      * Usually called by the {@link Roo.data.Store} which owns the Record.
13461      * Commits all changes made to the Record since either creation, or the last commit operation.
13462      * <p>
13463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464      * of commit operations.
13465      */
13466     commit : function(){
13467         this.dirty = false;
13468         delete this.modified;
13469         this.editing = false;
13470         if(this.store){
13471             this.store.afterCommit(this);
13472         }
13473     },
13474
13475     // private
13476     hasError : function(){
13477         return this.error != null;
13478     },
13479
13480     // private
13481     clearError : function(){
13482         this.error = null;
13483     },
13484
13485     /**
13486      * Creates a copy of this record.
13487      * @param {String} id (optional) A new record id if you don't want to use this record's id
13488      * @return {Record}
13489      */
13490     copy : function(newId) {
13491         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13492     }
13493 };/*
13494  * Based on:
13495  * Ext JS Library 1.1.1
13496  * Copyright(c) 2006-2007, Ext JS, LLC.
13497  *
13498  * Originally Released Under LGPL - original licence link has changed is not relivant.
13499  *
13500  * Fork - LGPL
13501  * <script type="text/javascript">
13502  */
13503
13504
13505
13506 /**
13507  * @class Roo.data.Store
13508  * @extends Roo.util.Observable
13509  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13510  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13511  * <p>
13512  * 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
13513  * has no knowledge of the format of the data returned by the Proxy.<br>
13514  * <p>
13515  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13516  * instances from the data object. These records are cached and made available through accessor functions.
13517  * @constructor
13518  * Creates a new Store.
13519  * @param {Object} config A config object containing the objects needed for the Store to access data,
13520  * and read the data into Records.
13521  */
13522 Roo.data.Store = function(config){
13523     this.data = new Roo.util.MixedCollection(false);
13524     this.data.getKey = function(o){
13525         return o.id;
13526     };
13527     this.baseParams = {};
13528     // private
13529     this.paramNames = {
13530         "start" : "start",
13531         "limit" : "limit",
13532         "sort" : "sort",
13533         "dir" : "dir",
13534         "multisort" : "_multisort"
13535     };
13536
13537     if(config && config.data){
13538         this.inlineData = config.data;
13539         delete config.data;
13540     }
13541
13542     Roo.apply(this, config);
13543     
13544     if(this.reader){ // reader passed
13545         this.reader = Roo.factory(this.reader, Roo.data);
13546         this.reader.xmodule = this.xmodule || false;
13547         if(!this.recordType){
13548             this.recordType = this.reader.recordType;
13549         }
13550         if(this.reader.onMetaChange){
13551             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13552         }
13553     }
13554
13555     if(this.recordType){
13556         this.fields = this.recordType.prototype.fields;
13557     }
13558     this.modified = [];
13559
13560     this.addEvents({
13561         /**
13562          * @event datachanged
13563          * Fires when the data cache has changed, and a widget which is using this Store
13564          * as a Record cache should refresh its view.
13565          * @param {Store} this
13566          */
13567         datachanged : true,
13568         /**
13569          * @event metachange
13570          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13571          * @param {Store} this
13572          * @param {Object} meta The JSON metadata
13573          */
13574         metachange : true,
13575         /**
13576          * @event add
13577          * Fires when Records have been added to the Store
13578          * @param {Store} this
13579          * @param {Roo.data.Record[]} records The array of Records added
13580          * @param {Number} index The index at which the record(s) were added
13581          */
13582         add : true,
13583         /**
13584          * @event remove
13585          * Fires when a Record has been removed from the Store
13586          * @param {Store} this
13587          * @param {Roo.data.Record} record The Record that was removed
13588          * @param {Number} index The index at which the record was removed
13589          */
13590         remove : true,
13591         /**
13592          * @event update
13593          * Fires when a Record has been updated
13594          * @param {Store} this
13595          * @param {Roo.data.Record} record The Record that was updated
13596          * @param {String} operation The update operation being performed.  Value may be one of:
13597          * <pre><code>
13598  Roo.data.Record.EDIT
13599  Roo.data.Record.REJECT
13600  Roo.data.Record.COMMIT
13601          * </code></pre>
13602          */
13603         update : true,
13604         /**
13605          * @event clear
13606          * Fires when the data cache has been cleared.
13607          * @param {Store} this
13608          */
13609         clear : true,
13610         /**
13611          * @event beforeload
13612          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13613          * the load action will be canceled.
13614          * @param {Store} this
13615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616          */
13617         beforeload : true,
13618         /**
13619          * @event beforeloadadd
13620          * Fires after a new set of Records has been loaded.
13621          * @param {Store} this
13622          * @param {Roo.data.Record[]} records The Records that were loaded
13623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13624          */
13625         beforeloadadd : true,
13626         /**
13627          * @event load
13628          * Fires after a new set of Records has been loaded, before they are added to the store.
13629          * @param {Store} this
13630          * @param {Roo.data.Record[]} records The Records that were loaded
13631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632          * @params {Object} return from reader
13633          */
13634         load : true,
13635         /**
13636          * @event loadexception
13637          * Fires if an exception occurs in the Proxy during loading.
13638          * Called with the signature of the Proxy's "loadexception" event.
13639          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13640          * 
13641          * @param {Proxy} 
13642          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13643          * @param {Object} load options 
13644          * @param {Object} jsonData from your request (normally this contains the Exception)
13645          */
13646         loadexception : true
13647     });
13648     
13649     if(this.proxy){
13650         this.proxy = Roo.factory(this.proxy, Roo.data);
13651         this.proxy.xmodule = this.xmodule || false;
13652         this.relayEvents(this.proxy,  ["loadexception"]);
13653     }
13654     this.sortToggle = {};
13655     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13656
13657     Roo.data.Store.superclass.constructor.call(this);
13658
13659     if(this.inlineData){
13660         this.loadData(this.inlineData);
13661         delete this.inlineData;
13662     }
13663 };
13664
13665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13666      /**
13667     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13668     * without a remote query - used by combo/forms at present.
13669     */
13670     
13671     /**
13672     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13673     */
13674     /**
13675     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13676     */
13677     /**
13678     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13679     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13680     */
13681     /**
13682     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13683     * on any HTTP request
13684     */
13685     /**
13686     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13687     */
13688     /**
13689     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13690     */
13691     multiSort: false,
13692     /**
13693     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13694     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13695     */
13696     remoteSort : false,
13697
13698     /**
13699     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13700      * loaded or when a record is removed. (defaults to false).
13701     */
13702     pruneModifiedRecords : false,
13703
13704     // private
13705     lastOptions : null,
13706
13707     /**
13708      * Add Records to the Store and fires the add event.
13709      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13710      */
13711     add : function(records){
13712         records = [].concat(records);
13713         for(var i = 0, len = records.length; i < len; i++){
13714             records[i].join(this);
13715         }
13716         var index = this.data.length;
13717         this.data.addAll(records);
13718         this.fireEvent("add", this, records, index);
13719     },
13720
13721     /**
13722      * Remove a Record from the Store and fires the remove event.
13723      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13724      */
13725     remove : function(record){
13726         var index = this.data.indexOf(record);
13727         this.data.removeAt(index);
13728  
13729         if(this.pruneModifiedRecords){
13730             this.modified.remove(record);
13731         }
13732         this.fireEvent("remove", this, record, index);
13733     },
13734
13735     /**
13736      * Remove all Records from the Store and fires the clear event.
13737      */
13738     removeAll : function(){
13739         this.data.clear();
13740         if(this.pruneModifiedRecords){
13741             this.modified = [];
13742         }
13743         this.fireEvent("clear", this);
13744     },
13745
13746     /**
13747      * Inserts Records to the Store at the given index and fires the add event.
13748      * @param {Number} index The start index at which to insert the passed Records.
13749      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13750      */
13751     insert : function(index, records){
13752         records = [].concat(records);
13753         for(var i = 0, len = records.length; i < len; i++){
13754             this.data.insert(index, records[i]);
13755             records[i].join(this);
13756         }
13757         this.fireEvent("add", this, records, index);
13758     },
13759
13760     /**
13761      * Get the index within the cache of the passed Record.
13762      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13763      * @return {Number} The index of the passed Record. Returns -1 if not found.
13764      */
13765     indexOf : function(record){
13766         return this.data.indexOf(record);
13767     },
13768
13769     /**
13770      * Get the index within the cache of the Record with the passed id.
13771      * @param {String} id The id of the Record to find.
13772      * @return {Number} The index of the Record. Returns -1 if not found.
13773      */
13774     indexOfId : function(id){
13775         return this.data.indexOfKey(id);
13776     },
13777
13778     /**
13779      * Get the Record with the specified id.
13780      * @param {String} id The id of the Record to find.
13781      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13782      */
13783     getById : function(id){
13784         return this.data.key(id);
13785     },
13786
13787     /**
13788      * Get the Record at the specified index.
13789      * @param {Number} index The index of the Record to find.
13790      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13791      */
13792     getAt : function(index){
13793         return this.data.itemAt(index);
13794     },
13795
13796     /**
13797      * Returns a range of Records between specified indices.
13798      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13799      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13800      * @return {Roo.data.Record[]} An array of Records
13801      */
13802     getRange : function(start, end){
13803         return this.data.getRange(start, end);
13804     },
13805
13806     // private
13807     storeOptions : function(o){
13808         o = Roo.apply({}, o);
13809         delete o.callback;
13810         delete o.scope;
13811         this.lastOptions = o;
13812     },
13813
13814     /**
13815      * Loads the Record cache from the configured Proxy using the configured Reader.
13816      * <p>
13817      * If using remote paging, then the first load call must specify the <em>start</em>
13818      * and <em>limit</em> properties in the options.params property to establish the initial
13819      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13820      * <p>
13821      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13822      * and this call will return before the new data has been loaded. Perform any post-processing
13823      * in a callback function, or in a "load" event handler.</strong>
13824      * <p>
13825      * @param {Object} options An object containing properties which control loading options:<ul>
13826      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13827      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13828      * passed the following arguments:<ul>
13829      * <li>r : Roo.data.Record[]</li>
13830      * <li>options: Options object from the load call</li>
13831      * <li>success: Boolean success indicator</li></ul></li>
13832      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13833      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13834      * </ul>
13835      */
13836     load : function(options){
13837         options = options || {};
13838         if(this.fireEvent("beforeload", this, options) !== false){
13839             this.storeOptions(options);
13840             var p = Roo.apply(options.params || {}, this.baseParams);
13841             // if meta was not loaded from remote source.. try requesting it.
13842             if (!this.reader.metaFromRemote) {
13843                 p._requestMeta = 1;
13844             }
13845             if(this.sortInfo && this.remoteSort){
13846                 var pn = this.paramNames;
13847                 p[pn["sort"]] = this.sortInfo.field;
13848                 p[pn["dir"]] = this.sortInfo.direction;
13849             }
13850             if (this.multiSort) {
13851                 var pn = this.paramNames;
13852                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13853             }
13854             
13855             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13856         }
13857     },
13858
13859     /**
13860      * Reloads the Record cache from the configured Proxy using the configured Reader and
13861      * the options from the last load operation performed.
13862      * @param {Object} options (optional) An object containing properties which may override the options
13863      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13864      * the most recently used options are reused).
13865      */
13866     reload : function(options){
13867         this.load(Roo.applyIf(options||{}, this.lastOptions));
13868     },
13869
13870     // private
13871     // Called as a callback by the Reader during a load operation.
13872     loadRecords : function(o, options, success){
13873         if(!o || success === false){
13874             if(success !== false){
13875                 this.fireEvent("load", this, [], options, o);
13876             }
13877             if(options.callback){
13878                 options.callback.call(options.scope || this, [], options, false);
13879             }
13880             return;
13881         }
13882         // if data returned failure - throw an exception.
13883         if (o.success === false) {
13884             // show a message if no listener is registered.
13885             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13886                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13887             }
13888             // loadmask wil be hooked into this..
13889             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13890             return;
13891         }
13892         var r = o.records, t = o.totalRecords || r.length;
13893         
13894         this.fireEvent("beforeloadadd", this, r, options, o);
13895         
13896         if(!options || options.add !== true){
13897             if(this.pruneModifiedRecords){
13898                 this.modified = [];
13899             }
13900             for(var i = 0, len = r.length; i < len; i++){
13901                 r[i].join(this);
13902             }
13903             if(this.snapshot){
13904                 this.data = this.snapshot;
13905                 delete this.snapshot;
13906             }
13907             this.data.clear();
13908             this.data.addAll(r);
13909             this.totalLength = t;
13910             this.applySort();
13911             this.fireEvent("datachanged", this);
13912         }else{
13913             this.totalLength = Math.max(t, this.data.length+r.length);
13914             this.add(r);
13915         }
13916         
13917         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13918                 
13919             var e = new Roo.data.Record({});
13920
13921             e.set(this.parent.displayField, this.parent.emptyTitle);
13922             e.set(this.parent.valueField, '');
13923
13924             this.insert(0, e);
13925         }
13926             
13927         this.fireEvent("load", this, r, options, o);
13928         if(options.callback){
13929             options.callback.call(options.scope || this, r, options, true);
13930         }
13931     },
13932
13933
13934     /**
13935      * Loads data from a passed data block. A Reader which understands the format of the data
13936      * must have been configured in the constructor.
13937      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13938      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13939      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13940      */
13941     loadData : function(o, append){
13942         var r = this.reader.readRecords(o);
13943         this.loadRecords(r, {add: append}, true);
13944     },
13945     
13946      /**
13947      * using 'cn' the nested child reader read the child array into it's child stores.
13948      * @param {Object} rec The record with a 'children array
13949      */
13950     loadDataFromChildren : function(rec)
13951     {
13952         this.loadData(this.reader.toLoadData(rec));
13953     },
13954     
13955
13956     /**
13957      * Gets the number of cached records.
13958      * <p>
13959      * <em>If using paging, this may not be the total size of the dataset. If the data object
13960      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13961      * the data set size</em>
13962      */
13963     getCount : function(){
13964         return this.data.length || 0;
13965     },
13966
13967     /**
13968      * Gets the total number of records in the dataset as returned by the server.
13969      * <p>
13970      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13971      * the dataset size</em>
13972      */
13973     getTotalCount : function(){
13974         return this.totalLength || 0;
13975     },
13976
13977     /**
13978      * Returns the sort state of the Store as an object with two properties:
13979      * <pre><code>
13980  field {String} The name of the field by which the Records are sorted
13981  direction {String} The sort order, "ASC" or "DESC"
13982      * </code></pre>
13983      */
13984     getSortState : function(){
13985         return this.sortInfo;
13986     },
13987
13988     // private
13989     applySort : function(){
13990         if(this.sortInfo && !this.remoteSort){
13991             var s = this.sortInfo, f = s.field;
13992             var st = this.fields.get(f).sortType;
13993             var fn = function(r1, r2){
13994                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13995                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13996             };
13997             this.data.sort(s.direction, fn);
13998             if(this.snapshot && this.snapshot != this.data){
13999                 this.snapshot.sort(s.direction, fn);
14000             }
14001         }
14002     },
14003
14004     /**
14005      * Sets the default sort column and order to be used by the next load operation.
14006      * @param {String} fieldName The name of the field to sort by.
14007      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14008      */
14009     setDefaultSort : function(field, dir){
14010         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14011     },
14012
14013     /**
14014      * Sort the Records.
14015      * If remote sorting is used, the sort is performed on the server, and the cache is
14016      * reloaded. If local sorting is used, the cache is sorted internally.
14017      * @param {String} fieldName The name of the field to sort by.
14018      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14019      */
14020     sort : function(fieldName, dir){
14021         var f = this.fields.get(fieldName);
14022         if(!dir){
14023             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14024             
14025             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14026                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14027             }else{
14028                 dir = f.sortDir;
14029             }
14030         }
14031         this.sortToggle[f.name] = dir;
14032         this.sortInfo = {field: f.name, direction: dir};
14033         if(!this.remoteSort){
14034             this.applySort();
14035             this.fireEvent("datachanged", this);
14036         }else{
14037             this.load(this.lastOptions);
14038         }
14039     },
14040
14041     /**
14042      * Calls the specified function for each of the Records in the cache.
14043      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14044      * Returning <em>false</em> aborts and exits the iteration.
14045      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14046      */
14047     each : function(fn, scope){
14048         this.data.each(fn, scope);
14049     },
14050
14051     /**
14052      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14053      * (e.g., during paging).
14054      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14055      */
14056     getModifiedRecords : function(){
14057         return this.modified;
14058     },
14059
14060     // private
14061     createFilterFn : function(property, value, anyMatch){
14062         if(!value.exec){ // not a regex
14063             value = String(value);
14064             if(value.length == 0){
14065                 return false;
14066             }
14067             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14068         }
14069         return function(r){
14070             return value.test(r.data[property]);
14071         };
14072     },
14073
14074     /**
14075      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14076      * @param {String} property A field on your records
14077      * @param {Number} start The record index to start at (defaults to 0)
14078      * @param {Number} end The last record index to include (defaults to length - 1)
14079      * @return {Number} The sum
14080      */
14081     sum : function(property, start, end){
14082         var rs = this.data.items, v = 0;
14083         start = start || 0;
14084         end = (end || end === 0) ? end : rs.length-1;
14085
14086         for(var i = start; i <= end; i++){
14087             v += (rs[i].data[property] || 0);
14088         }
14089         return v;
14090     },
14091
14092     /**
14093      * Filter the records by a specified property.
14094      * @param {String} field A field on your records
14095      * @param {String/RegExp} value Either a string that the field
14096      * should start with or a RegExp to test against the field
14097      * @param {Boolean} anyMatch True to match any part not just the beginning
14098      */
14099     filter : function(property, value, anyMatch){
14100         var fn = this.createFilterFn(property, value, anyMatch);
14101         return fn ? this.filterBy(fn) : this.clearFilter();
14102     },
14103
14104     /**
14105      * Filter by a function. The specified function will be called with each
14106      * record in this data source. If the function returns true the record is included,
14107      * otherwise it is filtered.
14108      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14109      * @param {Object} scope (optional) The scope of the function (defaults to this)
14110      */
14111     filterBy : function(fn, scope){
14112         this.snapshot = this.snapshot || this.data;
14113         this.data = this.queryBy(fn, scope||this);
14114         this.fireEvent("datachanged", this);
14115     },
14116
14117     /**
14118      * Query the records by a specified property.
14119      * @param {String} field A field on your records
14120      * @param {String/RegExp} value Either a string that the field
14121      * should start with or a RegExp to test against the field
14122      * @param {Boolean} anyMatch True to match any part not just the beginning
14123      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14124      */
14125     query : function(property, value, anyMatch){
14126         var fn = this.createFilterFn(property, value, anyMatch);
14127         return fn ? this.queryBy(fn) : this.data.clone();
14128     },
14129
14130     /**
14131      * Query by a function. The specified function will be called with each
14132      * record in this data source. If the function returns true the record is included
14133      * in the results.
14134      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14135      * @param {Object} scope (optional) The scope of the function (defaults to this)
14136       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14137      **/
14138     queryBy : function(fn, scope){
14139         var data = this.snapshot || this.data;
14140         return data.filterBy(fn, scope||this);
14141     },
14142
14143     /**
14144      * Collects unique values for a particular dataIndex from this store.
14145      * @param {String} dataIndex The property to collect
14146      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14147      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14148      * @return {Array} An array of the unique values
14149      **/
14150     collect : function(dataIndex, allowNull, bypassFilter){
14151         var d = (bypassFilter === true && this.snapshot) ?
14152                 this.snapshot.items : this.data.items;
14153         var v, sv, r = [], l = {};
14154         for(var i = 0, len = d.length; i < len; i++){
14155             v = d[i].data[dataIndex];
14156             sv = String(v);
14157             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14158                 l[sv] = true;
14159                 r[r.length] = v;
14160             }
14161         }
14162         return r;
14163     },
14164
14165     /**
14166      * Revert to a view of the Record cache with no filtering applied.
14167      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14168      */
14169     clearFilter : function(suppressEvent){
14170         if(this.snapshot && this.snapshot != this.data){
14171             this.data = this.snapshot;
14172             delete this.snapshot;
14173             if(suppressEvent !== true){
14174                 this.fireEvent("datachanged", this);
14175             }
14176         }
14177     },
14178
14179     // private
14180     afterEdit : function(record){
14181         if(this.modified.indexOf(record) == -1){
14182             this.modified.push(record);
14183         }
14184         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14185     },
14186     
14187     // private
14188     afterReject : function(record){
14189         this.modified.remove(record);
14190         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14191     },
14192
14193     // private
14194     afterCommit : function(record){
14195         this.modified.remove(record);
14196         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14197     },
14198
14199     /**
14200      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14201      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14202      */
14203     commitChanges : function(){
14204         var m = this.modified.slice(0);
14205         this.modified = [];
14206         for(var i = 0, len = m.length; i < len; i++){
14207             m[i].commit();
14208         }
14209     },
14210
14211     /**
14212      * Cancel outstanding changes on all changed records.
14213      */
14214     rejectChanges : function(){
14215         var m = this.modified.slice(0);
14216         this.modified = [];
14217         for(var i = 0, len = m.length; i < len; i++){
14218             m[i].reject();
14219         }
14220     },
14221
14222     onMetaChange : function(meta, rtype, o){
14223         this.recordType = rtype;
14224         this.fields = rtype.prototype.fields;
14225         delete this.snapshot;
14226         this.sortInfo = meta.sortInfo || this.sortInfo;
14227         this.modified = [];
14228         this.fireEvent('metachange', this, this.reader.meta);
14229     },
14230     
14231     moveIndex : function(data, type)
14232     {
14233         var index = this.indexOf(data);
14234         
14235         var newIndex = index + type;
14236         
14237         this.remove(data);
14238         
14239         this.insert(newIndex, data);
14240         
14241     }
14242 });/*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252
14253 /**
14254  * @class Roo.data.SimpleStore
14255  * @extends Roo.data.Store
14256  * Small helper class to make creating Stores from Array data easier.
14257  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14258  * @cfg {Array} fields An array of field definition objects, or field name strings.
14259  * @cfg {Object} an existing reader (eg. copied from another store)
14260  * @cfg {Array} data The multi-dimensional array of data
14261  * @constructor
14262  * @param {Object} config
14263  */
14264 Roo.data.SimpleStore = function(config)
14265 {
14266     Roo.data.SimpleStore.superclass.constructor.call(this, {
14267         isLocal : true,
14268         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14269                 id: config.id
14270             },
14271             Roo.data.Record.create(config.fields)
14272         ),
14273         proxy : new Roo.data.MemoryProxy(config.data)
14274     });
14275     this.load();
14276 };
14277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287
14288 /**
14289 /**
14290  * @extends Roo.data.Store
14291  * @class Roo.data.JsonStore
14292  * Small helper class to make creating Stores for JSON data easier. <br/>
14293 <pre><code>
14294 var store = new Roo.data.JsonStore({
14295     url: 'get-images.php',
14296     root: 'images',
14297     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14298 });
14299 </code></pre>
14300  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14301  * JsonReader and HttpProxy (unless inline data is provided).</b>
14302  * @cfg {Array} fields An array of field definition objects, or field name strings.
14303  * @constructor
14304  * @param {Object} config
14305  */
14306 Roo.data.JsonStore = function(c){
14307     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14308         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14309         reader: new Roo.data.JsonReader(c, c.fields)
14310     }));
14311 };
14312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14313  * Based on:
14314  * Ext JS Library 1.1.1
14315  * Copyright(c) 2006-2007, Ext JS, LLC.
14316  *
14317  * Originally Released Under LGPL - original licence link has changed is not relivant.
14318  *
14319  * Fork - LGPL
14320  * <script type="text/javascript">
14321  */
14322
14323  
14324 Roo.data.Field = function(config){
14325     if(typeof config == "string"){
14326         config = {name: config};
14327     }
14328     Roo.apply(this, config);
14329     
14330     if(!this.type){
14331         this.type = "auto";
14332     }
14333     
14334     var st = Roo.data.SortTypes;
14335     // named sortTypes are supported, here we look them up
14336     if(typeof this.sortType == "string"){
14337         this.sortType = st[this.sortType];
14338     }
14339     
14340     // set default sortType for strings and dates
14341     if(!this.sortType){
14342         switch(this.type){
14343             case "string":
14344                 this.sortType = st.asUCString;
14345                 break;
14346             case "date":
14347                 this.sortType = st.asDate;
14348                 break;
14349             default:
14350                 this.sortType = st.none;
14351         }
14352     }
14353
14354     // define once
14355     var stripRe = /[\$,%]/g;
14356
14357     // prebuilt conversion function for this field, instead of
14358     // switching every time we're reading a value
14359     if(!this.convert){
14360         var cv, dateFormat = this.dateFormat;
14361         switch(this.type){
14362             case "":
14363             case "auto":
14364             case undefined:
14365                 cv = function(v){ return v; };
14366                 break;
14367             case "string":
14368                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14369                 break;
14370             case "int":
14371                 cv = function(v){
14372                     return v !== undefined && v !== null && v !== '' ?
14373                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14374                     };
14375                 break;
14376             case "float":
14377                 cv = function(v){
14378                     return v !== undefined && v !== null && v !== '' ?
14379                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14380                     };
14381                 break;
14382             case "bool":
14383             case "boolean":
14384                 cv = function(v){ return v === true || v === "true" || v == 1; };
14385                 break;
14386             case "date":
14387                 cv = function(v){
14388                     if(!v){
14389                         return '';
14390                     }
14391                     if(v instanceof Date){
14392                         return v;
14393                     }
14394                     if(dateFormat){
14395                         if(dateFormat == "timestamp"){
14396                             return new Date(v*1000);
14397                         }
14398                         return Date.parseDate(v, dateFormat);
14399                     }
14400                     var parsed = Date.parse(v);
14401                     return parsed ? new Date(parsed) : null;
14402                 };
14403              break;
14404             
14405         }
14406         this.convert = cv;
14407     }
14408 };
14409
14410 Roo.data.Field.prototype = {
14411     dateFormat: null,
14412     defaultValue: "",
14413     mapping: null,
14414     sortType : null,
14415     sortDir : "ASC"
14416 };/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426  
14427 // Base class for reading structured data from a data source.  This class is intended to be
14428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14429
14430 /**
14431  * @class Roo.data.DataReader
14432  * Base class for reading structured data from a data source.  This class is intended to be
14433  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14434  */
14435
14436 Roo.data.DataReader = function(meta, recordType){
14437     
14438     this.meta = meta;
14439     
14440     this.recordType = recordType instanceof Array ? 
14441         Roo.data.Record.create(recordType) : recordType;
14442 };
14443
14444 Roo.data.DataReader.prototype = {
14445     
14446     
14447     readerType : 'Data',
14448      /**
14449      * Create an empty record
14450      * @param {Object} data (optional) - overlay some values
14451      * @return {Roo.data.Record} record created.
14452      */
14453     newRow :  function(d) {
14454         var da =  {};
14455         this.recordType.prototype.fields.each(function(c) {
14456             switch( c.type) {
14457                 case 'int' : da[c.name] = 0; break;
14458                 case 'date' : da[c.name] = new Date(); break;
14459                 case 'float' : da[c.name] = 0.0; break;
14460                 case 'boolean' : da[c.name] = false; break;
14461                 default : da[c.name] = ""; break;
14462             }
14463             
14464         });
14465         return new this.recordType(Roo.apply(da, d));
14466     }
14467     
14468     
14469 };/*
14470  * Based on:
14471  * Ext JS Library 1.1.1
14472  * Copyright(c) 2006-2007, Ext JS, LLC.
14473  *
14474  * Originally Released Under LGPL - original licence link has changed is not relivant.
14475  *
14476  * Fork - LGPL
14477  * <script type="text/javascript">
14478  */
14479
14480 /**
14481  * @class Roo.data.DataProxy
14482  * @extends Roo.data.Observable
14483  * This class is an abstract base class for implementations which provide retrieval of
14484  * unformatted data objects.<br>
14485  * <p>
14486  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14487  * (of the appropriate type which knows how to parse the data object) to provide a block of
14488  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14489  * <p>
14490  * Custom implementations must implement the load method as described in
14491  * {@link Roo.data.HttpProxy#load}.
14492  */
14493 Roo.data.DataProxy = function(){
14494     this.addEvents({
14495         /**
14496          * @event beforeload
14497          * Fires before a network request is made to retrieve a data object.
14498          * @param {Object} This DataProxy object.
14499          * @param {Object} params The params parameter to the load function.
14500          */
14501         beforeload : true,
14502         /**
14503          * @event load
14504          * Fires before the load method's callback is called.
14505          * @param {Object} This DataProxy object.
14506          * @param {Object} o The data object.
14507          * @param {Object} arg The callback argument object passed to the load function.
14508          */
14509         load : true,
14510         /**
14511          * @event loadexception
14512          * Fires if an Exception occurs during data retrieval.
14513          * @param {Object} This DataProxy object.
14514          * @param {Object} o The data object.
14515          * @param {Object} arg The callback argument object passed to the load function.
14516          * @param {Object} e The Exception.
14517          */
14518         loadexception : true
14519     });
14520     Roo.data.DataProxy.superclass.constructor.call(this);
14521 };
14522
14523 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14524
14525     /**
14526      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14527      */
14528 /*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538 /**
14539  * @class Roo.data.MemoryProxy
14540  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14541  * to the Reader when its load method is called.
14542  * @constructor
14543  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14544  */
14545 Roo.data.MemoryProxy = function(data){
14546     if (data.data) {
14547         data = data.data;
14548     }
14549     Roo.data.MemoryProxy.superclass.constructor.call(this);
14550     this.data = data;
14551 };
14552
14553 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14554     
14555     /**
14556      * Load data from the requested source (in this case an in-memory
14557      * data object passed to the constructor), read the data object into
14558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14559      * process that block using the passed callback.
14560      * @param {Object} params This parameter is not used by the MemoryProxy class.
14561      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14562      * object into a block of Roo.data.Records.
14563      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14564      * The function must be passed <ul>
14565      * <li>The Record block object</li>
14566      * <li>The "arg" argument from the load function</li>
14567      * <li>A boolean success indicator</li>
14568      * </ul>
14569      * @param {Object} scope The scope in which to call the callback
14570      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14571      */
14572     load : function(params, reader, callback, scope, arg){
14573         params = params || {};
14574         var result;
14575         try {
14576             result = reader.readRecords(params.data ? params.data :this.data);
14577         }catch(e){
14578             this.fireEvent("loadexception", this, arg, null, e);
14579             callback.call(scope, null, arg, false);
14580             return;
14581         }
14582         callback.call(scope, result, arg, true);
14583     },
14584     
14585     // private
14586     update : function(params, records){
14587         
14588     }
14589 });/*
14590  * Based on:
14591  * Ext JS Library 1.1.1
14592  * Copyright(c) 2006-2007, Ext JS, LLC.
14593  *
14594  * Originally Released Under LGPL - original licence link has changed is not relivant.
14595  *
14596  * Fork - LGPL
14597  * <script type="text/javascript">
14598  */
14599 /**
14600  * @class Roo.data.HttpProxy
14601  * @extends Roo.data.DataProxy
14602  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14603  * configured to reference a certain URL.<br><br>
14604  * <p>
14605  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14606  * from which the running page was served.<br><br>
14607  * <p>
14608  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14609  * <p>
14610  * Be aware that to enable the browser to parse an XML document, the server must set
14611  * the Content-Type header in the HTTP response to "text/xml".
14612  * @constructor
14613  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14614  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14615  * will be used to make the request.
14616  */
14617 Roo.data.HttpProxy = function(conn){
14618     Roo.data.HttpProxy.superclass.constructor.call(this);
14619     // is conn a conn config or a real conn?
14620     this.conn = conn;
14621     this.useAjax = !conn || !conn.events;
14622   
14623 };
14624
14625 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14626     // thse are take from connection...
14627     
14628     /**
14629      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14630      */
14631     /**
14632      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14633      * extra parameters to each request made by this object. (defaults to undefined)
14634      */
14635     /**
14636      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14637      *  to each request made by this object. (defaults to undefined)
14638      */
14639     /**
14640      * @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)
14641      */
14642     /**
14643      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14644      */
14645      /**
14646      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14647      * @type Boolean
14648      */
14649   
14650
14651     /**
14652      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14653      * @type Boolean
14654      */
14655     /**
14656      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14657      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14658      * a finer-grained basis than the DataProxy events.
14659      */
14660     getConnection : function(){
14661         return this.useAjax ? Roo.Ajax : this.conn;
14662     },
14663
14664     /**
14665      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14666      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14667      * process that block using the passed callback.
14668      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14669      * for the request to the remote server.
14670      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14671      * object into a block of Roo.data.Records.
14672      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14673      * The function must be passed <ul>
14674      * <li>The Record block object</li>
14675      * <li>The "arg" argument from the load function</li>
14676      * <li>A boolean success indicator</li>
14677      * </ul>
14678      * @param {Object} scope The scope in which to call the callback
14679      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14680      */
14681     load : function(params, reader, callback, scope, arg){
14682         if(this.fireEvent("beforeload", this, params) !== false){
14683             var  o = {
14684                 params : params || {},
14685                 request: {
14686                     callback : callback,
14687                     scope : scope,
14688                     arg : arg
14689                 },
14690                 reader: reader,
14691                 callback : this.loadResponse,
14692                 scope: this
14693             };
14694             if(this.useAjax){
14695                 Roo.applyIf(o, this.conn);
14696                 if(this.activeRequest){
14697                     Roo.Ajax.abort(this.activeRequest);
14698                 }
14699                 this.activeRequest = Roo.Ajax.request(o);
14700             }else{
14701                 this.conn.request(o);
14702             }
14703         }else{
14704             callback.call(scope||this, null, arg, false);
14705         }
14706     },
14707
14708     // private
14709     loadResponse : function(o, success, response){
14710         delete this.activeRequest;
14711         if(!success){
14712             this.fireEvent("loadexception", this, o, response);
14713             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14714             return;
14715         }
14716         var result;
14717         try {
14718             result = o.reader.read(response);
14719         }catch(e){
14720             this.fireEvent("loadexception", this, o, response, e);
14721             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14722             return;
14723         }
14724         
14725         this.fireEvent("load", this, o, o.request.arg);
14726         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14727     },
14728
14729     // private
14730     update : function(dataSet){
14731
14732     },
14733
14734     // private
14735     updateResponse : function(dataSet){
14736
14737     }
14738 });/*
14739  * Based on:
14740  * Ext JS Library 1.1.1
14741  * Copyright(c) 2006-2007, Ext JS, LLC.
14742  *
14743  * Originally Released Under LGPL - original licence link has changed is not relivant.
14744  *
14745  * Fork - LGPL
14746  * <script type="text/javascript">
14747  */
14748
14749 /**
14750  * @class Roo.data.ScriptTagProxy
14751  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14752  * other than the originating domain of the running page.<br><br>
14753  * <p>
14754  * <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
14755  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14756  * <p>
14757  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14758  * source code that is used as the source inside a &lt;script> tag.<br><br>
14759  * <p>
14760  * In order for the browser to process the returned data, the server must wrap the data object
14761  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14762  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14763  * depending on whether the callback name was passed:
14764  * <p>
14765  * <pre><code>
14766 boolean scriptTag = false;
14767 String cb = request.getParameter("callback");
14768 if (cb != null) {
14769     scriptTag = true;
14770     response.setContentType("text/javascript");
14771 } else {
14772     response.setContentType("application/x-json");
14773 }
14774 Writer out = response.getWriter();
14775 if (scriptTag) {
14776     out.write(cb + "(");
14777 }
14778 out.print(dataBlock.toJsonString());
14779 if (scriptTag) {
14780     out.write(");");
14781 }
14782 </pre></code>
14783  *
14784  * @constructor
14785  * @param {Object} config A configuration object.
14786  */
14787 Roo.data.ScriptTagProxy = function(config){
14788     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14789     Roo.apply(this, config);
14790     this.head = document.getElementsByTagName("head")[0];
14791 };
14792
14793 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14794
14795 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14796     /**
14797      * @cfg {String} url The URL from which to request the data object.
14798      */
14799     /**
14800      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14801      */
14802     timeout : 30000,
14803     /**
14804      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14805      * the server the name of the callback function set up by the load call to process the returned data object.
14806      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14807      * javascript output which calls this named function passing the data object as its only parameter.
14808      */
14809     callbackParam : "callback",
14810     /**
14811      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14812      * name to the request.
14813      */
14814     nocache : true,
14815
14816     /**
14817      * Load data from the configured URL, read the data object into
14818      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14819      * process that block using the passed callback.
14820      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14821      * for the request to the remote server.
14822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14823      * object into a block of Roo.data.Records.
14824      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14825      * The function must be passed <ul>
14826      * <li>The Record block object</li>
14827      * <li>The "arg" argument from the load function</li>
14828      * <li>A boolean success indicator</li>
14829      * </ul>
14830      * @param {Object} scope The scope in which to call the callback
14831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14832      */
14833     load : function(params, reader, callback, scope, arg){
14834         if(this.fireEvent("beforeload", this, params) !== false){
14835
14836             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14837
14838             var url = this.url;
14839             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14840             if(this.nocache){
14841                 url += "&_dc=" + (new Date().getTime());
14842             }
14843             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14844             var trans = {
14845                 id : transId,
14846                 cb : "stcCallback"+transId,
14847                 scriptId : "stcScript"+transId,
14848                 params : params,
14849                 arg : arg,
14850                 url : url,
14851                 callback : callback,
14852                 scope : scope,
14853                 reader : reader
14854             };
14855             var conn = this;
14856
14857             window[trans.cb] = function(o){
14858                 conn.handleResponse(o, trans);
14859             };
14860
14861             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14862
14863             if(this.autoAbort !== false){
14864                 this.abort();
14865             }
14866
14867             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14868
14869             var script = document.createElement("script");
14870             script.setAttribute("src", url);
14871             script.setAttribute("type", "text/javascript");
14872             script.setAttribute("id", trans.scriptId);
14873             this.head.appendChild(script);
14874
14875             this.trans = trans;
14876         }else{
14877             callback.call(scope||this, null, arg, false);
14878         }
14879     },
14880
14881     // private
14882     isLoading : function(){
14883         return this.trans ? true : false;
14884     },
14885
14886     /**
14887      * Abort the current server request.
14888      */
14889     abort : function(){
14890         if(this.isLoading()){
14891             this.destroyTrans(this.trans);
14892         }
14893     },
14894
14895     // private
14896     destroyTrans : function(trans, isLoaded){
14897         this.head.removeChild(document.getElementById(trans.scriptId));
14898         clearTimeout(trans.timeoutId);
14899         if(isLoaded){
14900             window[trans.cb] = undefined;
14901             try{
14902                 delete window[trans.cb];
14903             }catch(e){}
14904         }else{
14905             // if hasn't been loaded, wait for load to remove it to prevent script error
14906             window[trans.cb] = function(){
14907                 window[trans.cb] = undefined;
14908                 try{
14909                     delete window[trans.cb];
14910                 }catch(e){}
14911             };
14912         }
14913     },
14914
14915     // private
14916     handleResponse : function(o, trans){
14917         this.trans = false;
14918         this.destroyTrans(trans, true);
14919         var result;
14920         try {
14921             result = trans.reader.readRecords(o);
14922         }catch(e){
14923             this.fireEvent("loadexception", this, o, trans.arg, e);
14924             trans.callback.call(trans.scope||window, null, trans.arg, false);
14925             return;
14926         }
14927         this.fireEvent("load", this, o, trans.arg);
14928         trans.callback.call(trans.scope||window, result, trans.arg, true);
14929     },
14930
14931     // private
14932     handleFailure : function(trans){
14933         this.trans = false;
14934         this.destroyTrans(trans, false);
14935         this.fireEvent("loadexception", this, null, trans.arg);
14936         trans.callback.call(trans.scope||window, null, trans.arg, false);
14937     }
14938 });/*
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  * @class Roo.data.JsonReader
14951  * @extends Roo.data.DataReader
14952  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14953  * based on mappings in a provided Roo.data.Record constructor.
14954  * 
14955  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14956  * in the reply previously. 
14957  * 
14958  * <p>
14959  * Example code:
14960  * <pre><code>
14961 var RecordDef = Roo.data.Record.create([
14962     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14963     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14964 ]);
14965 var myReader = new Roo.data.JsonReader({
14966     totalProperty: "results",    // The property which contains the total dataset size (optional)
14967     root: "rows",                // The property which contains an Array of row objects
14968     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14969 }, RecordDef);
14970 </code></pre>
14971  * <p>
14972  * This would consume a JSON file like this:
14973  * <pre><code>
14974 { 'results': 2, 'rows': [
14975     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14976     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14977 }
14978 </code></pre>
14979  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14980  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14981  * paged from the remote server.
14982  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14983  * @cfg {String} root name of the property which contains the Array of row objects.
14984  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14985  * @cfg {Array} fields Array of field definition objects
14986  * @constructor
14987  * Create a new JsonReader
14988  * @param {Object} meta Metadata configuration options
14989  * @param {Object} recordType Either an Array of field definition objects,
14990  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14991  */
14992 Roo.data.JsonReader = function(meta, recordType){
14993     
14994     meta = meta || {};
14995     // set some defaults:
14996     Roo.applyIf(meta, {
14997         totalProperty: 'total',
14998         successProperty : 'success',
14999         root : 'data',
15000         id : 'id'
15001     });
15002     
15003     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15004 };
15005 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15006     
15007     readerType : 'Json',
15008     
15009     /**
15010      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15011      * Used by Store query builder to append _requestMeta to params.
15012      * 
15013      */
15014     metaFromRemote : false,
15015     /**
15016      * This method is only used by a DataProxy which has retrieved data from a remote server.
15017      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15018      * @return {Object} data A data block which is used by an Roo.data.Store object as
15019      * a cache of Roo.data.Records.
15020      */
15021     read : function(response){
15022         var json = response.responseText;
15023        
15024         var o = /* eval:var:o */ eval("("+json+")");
15025         if(!o) {
15026             throw {message: "JsonReader.read: Json object not found"};
15027         }
15028         
15029         if(o.metaData){
15030             
15031             delete this.ef;
15032             this.metaFromRemote = true;
15033             this.meta = o.metaData;
15034             this.recordType = Roo.data.Record.create(o.metaData.fields);
15035             this.onMetaChange(this.meta, this.recordType, o);
15036         }
15037         return this.readRecords(o);
15038     },
15039
15040     // private function a store will implement
15041     onMetaChange : function(meta, recordType, o){
15042
15043     },
15044
15045     /**
15046          * @ignore
15047          */
15048     simpleAccess: function(obj, subsc) {
15049         return obj[subsc];
15050     },
15051
15052         /**
15053          * @ignore
15054          */
15055     getJsonAccessor: function(){
15056         var re = /[\[\.]/;
15057         return function(expr) {
15058             try {
15059                 return(re.test(expr))
15060                     ? new Function("obj", "return obj." + expr)
15061                     : function(obj){
15062                         return obj[expr];
15063                     };
15064             } catch(e){}
15065             return Roo.emptyFn;
15066         };
15067     }(),
15068
15069     /**
15070      * Create a data block containing Roo.data.Records from an XML document.
15071      * @param {Object} o An object which contains an Array of row objects in the property specified
15072      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15073      * which contains the total size of the dataset.
15074      * @return {Object} data A data block which is used by an Roo.data.Store object as
15075      * a cache of Roo.data.Records.
15076      */
15077     readRecords : function(o){
15078         /**
15079          * After any data loads, the raw JSON data is available for further custom processing.
15080          * @type Object
15081          */
15082         this.o = o;
15083         var s = this.meta, Record = this.recordType,
15084             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15085
15086 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15087         if (!this.ef) {
15088             if(s.totalProperty) {
15089                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15090                 }
15091                 if(s.successProperty) {
15092                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15093                 }
15094                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15095                 if (s.id) {
15096                         var g = this.getJsonAccessor(s.id);
15097                         this.getId = function(rec) {
15098                                 var r = g(rec);  
15099                                 return (r === undefined || r === "") ? null : r;
15100                         };
15101                 } else {
15102                         this.getId = function(){return null;};
15103                 }
15104             this.ef = [];
15105             for(var jj = 0; jj < fl; jj++){
15106                 f = fi[jj];
15107                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15108                 this.ef[jj] = this.getJsonAccessor(map);
15109             }
15110         }
15111
15112         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15113         if(s.totalProperty){
15114             var vt = parseInt(this.getTotal(o), 10);
15115             if(!isNaN(vt)){
15116                 totalRecords = vt;
15117             }
15118         }
15119         if(s.successProperty){
15120             var vs = this.getSuccess(o);
15121             if(vs === false || vs === 'false'){
15122                 success = false;
15123             }
15124         }
15125         var records = [];
15126         for(var i = 0; i < c; i++){
15127                 var n = root[i];
15128             var values = {};
15129             var id = this.getId(n);
15130             for(var j = 0; j < fl; j++){
15131                 f = fi[j];
15132             var v = this.ef[j](n);
15133             if (!f.convert) {
15134                 Roo.log('missing convert for ' + f.name);
15135                 Roo.log(f);
15136                 continue;
15137             }
15138             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15139             }
15140             var record = new Record(values, id);
15141             record.json = n;
15142             records[i] = record;
15143         }
15144         return {
15145             raw : o,
15146             success : success,
15147             records : records,
15148             totalRecords : totalRecords
15149         };
15150     },
15151     // used when loading children.. @see loadDataFromChildren
15152     toLoadData: function(rec)
15153     {
15154         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15155         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15156         return { data : data, total : data.length };
15157         
15158     }
15159 });/*
15160  * Based on:
15161  * Ext JS Library 1.1.1
15162  * Copyright(c) 2006-2007, Ext JS, LLC.
15163  *
15164  * Originally Released Under LGPL - original licence link has changed is not relivant.
15165  *
15166  * Fork - LGPL
15167  * <script type="text/javascript">
15168  */
15169
15170 /**
15171  * @class Roo.data.ArrayReader
15172  * @extends Roo.data.DataReader
15173  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15174  * Each element of that Array represents a row of data fields. The
15175  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15176  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15177  * <p>
15178  * Example code:.
15179  * <pre><code>
15180 var RecordDef = Roo.data.Record.create([
15181     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15182     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15183 ]);
15184 var myReader = new Roo.data.ArrayReader({
15185     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15186 }, RecordDef);
15187 </code></pre>
15188  * <p>
15189  * This would consume an Array like this:
15190  * <pre><code>
15191 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15192   </code></pre>
15193  
15194  * @constructor
15195  * Create a new JsonReader
15196  * @param {Object} meta Metadata configuration options.
15197  * @param {Object|Array} recordType Either an Array of field definition objects
15198  * 
15199  * @cfg {Array} fields Array of field definition objects
15200  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15201  * as specified to {@link Roo.data.Record#create},
15202  * or an {@link Roo.data.Record} object
15203  *
15204  * 
15205  * created using {@link Roo.data.Record#create}.
15206  */
15207 Roo.data.ArrayReader = function(meta, recordType)
15208 {    
15209     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15210 };
15211
15212 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15213     
15214       /**
15215      * Create a data block containing Roo.data.Records from an XML document.
15216      * @param {Object} o An Array of row objects which represents the dataset.
15217      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15218      * a cache of Roo.data.Records.
15219      */
15220     readRecords : function(o)
15221     {
15222         var sid = this.meta ? this.meta.id : null;
15223         var recordType = this.recordType, fields = recordType.prototype.fields;
15224         var records = [];
15225         var root = o;
15226         for(var i = 0; i < root.length; i++){
15227             var n = root[i];
15228             var values = {};
15229             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15230             for(var j = 0, jlen = fields.length; j < jlen; j++){
15231                 var f = fields.items[j];
15232                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15233                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15234                 v = f.convert(v);
15235                 values[f.name] = v;
15236             }
15237             var record = new recordType(values, id);
15238             record.json = n;
15239             records[records.length] = record;
15240         }
15241         return {
15242             records : records,
15243             totalRecords : records.length
15244         };
15245     },
15246     // used when loading children.. @see loadDataFromChildren
15247     toLoadData: function(rec)
15248     {
15249         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15250         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15251         
15252     }
15253     
15254     
15255 });/*
15256  * - LGPL
15257  * * 
15258  */
15259
15260 /**
15261  * @class Roo.bootstrap.ComboBox
15262  * @extends Roo.bootstrap.TriggerField
15263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15264  * @cfg {Boolean} append (true|false) default false
15265  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15266  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15267  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15268  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15269  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15270  * @cfg {Boolean} animate default true
15271  * @cfg {Boolean} emptyResultText only for touch device
15272  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15273  * @cfg {String} emptyTitle default ''
15274  * @cfg {Number} width fixed with? experimental
15275  * @constructor
15276  * Create a new ComboBox.
15277  * @param {Object} config Configuration options
15278  */
15279 Roo.bootstrap.ComboBox = function(config){
15280     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15281     this.addEvents({
15282         /**
15283          * @event expand
15284          * Fires when the dropdown list is expanded
15285         * @param {Roo.bootstrap.ComboBox} combo This combo box
15286         */
15287         'expand' : true,
15288         /**
15289          * @event collapse
15290          * Fires when the dropdown list is collapsed
15291         * @param {Roo.bootstrap.ComboBox} combo This combo box
15292         */
15293         'collapse' : true,
15294         /**
15295          * @event beforeselect
15296          * Fires before a list item is selected. Return false to cancel the selection.
15297         * @param {Roo.bootstrap.ComboBox} combo This combo box
15298         * @param {Roo.data.Record} record The data record returned from the underlying store
15299         * @param {Number} index The index of the selected item in the dropdown list
15300         */
15301         'beforeselect' : true,
15302         /**
15303          * @event select
15304          * Fires when a list item is selected
15305         * @param {Roo.bootstrap.ComboBox} combo This combo box
15306         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15307         * @param {Number} index The index of the selected item in the dropdown list
15308         */
15309         'select' : true,
15310         /**
15311          * @event beforequery
15312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15313          * The event object passed has these properties:
15314         * @param {Roo.bootstrap.ComboBox} combo This combo box
15315         * @param {String} query The query
15316         * @param {Boolean} forceAll true to force "all" query
15317         * @param {Boolean} cancel true to cancel the query
15318         * @param {Object} e The query event object
15319         */
15320         'beforequery': true,
15321          /**
15322          * @event add
15323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         */
15326         'add' : true,
15327         /**
15328          * @event edit
15329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15330         * @param {Roo.bootstrap.ComboBox} combo This combo box
15331         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15332         */
15333         'edit' : true,
15334         /**
15335          * @event remove
15336          * Fires when the remove value from the combobox array
15337         * @param {Roo.bootstrap.ComboBox} combo This combo box
15338         */
15339         'remove' : true,
15340         /**
15341          * @event afterremove
15342          * Fires when the remove value from the combobox array
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         */
15345         'afterremove' : true,
15346         /**
15347          * @event specialfilter
15348          * Fires when specialfilter
15349             * @param {Roo.bootstrap.ComboBox} combo This combo box
15350             */
15351         'specialfilter' : true,
15352         /**
15353          * @event tick
15354          * Fires when tick the element
15355             * @param {Roo.bootstrap.ComboBox} combo This combo box
15356             */
15357         'tick' : true,
15358         /**
15359          * @event touchviewdisplay
15360          * Fires when touch view require special display (default is using displayField)
15361             * @param {Roo.bootstrap.ComboBox} combo This combo box
15362             * @param {Object} cfg set html .
15363             */
15364         'touchviewdisplay' : true
15365         
15366     });
15367     
15368     this.item = [];
15369     this.tickItems = [];
15370     
15371     this.selectedIndex = -1;
15372     if(this.mode == 'local'){
15373         if(config.queryDelay === undefined){
15374             this.queryDelay = 10;
15375         }
15376         if(config.minChars === undefined){
15377             this.minChars = 0;
15378         }
15379     }
15380 };
15381
15382 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15383      
15384     /**
15385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15386      * rendering into an Roo.Editor, defaults to false)
15387      */
15388     /**
15389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15391      */
15392     /**
15393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15394      */
15395     /**
15396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15397      * the dropdown list (defaults to undefined, with no header element)
15398      */
15399
15400      /**
15401      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15402      */
15403      
15404      /**
15405      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15406      */
15407     listWidth: undefined,
15408     /**
15409      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15410      * mode = 'remote' or 'text' if mode = 'local')
15411      */
15412     displayField: undefined,
15413     
15414     /**
15415      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15416      * mode = 'remote' or 'value' if mode = 'local'). 
15417      * Note: use of a valueField requires the user make a selection
15418      * in order for a value to be mapped.
15419      */
15420     valueField: undefined,
15421     /**
15422      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15423      */
15424     modalTitle : '',
15425     
15426     /**
15427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15428      * field's data value (defaults to the underlying DOM element's name)
15429      */
15430     hiddenName: undefined,
15431     /**
15432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15433      */
15434     listClass: '',
15435     /**
15436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15437      */
15438     selectedClass: 'active',
15439     
15440     /**
15441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15442      */
15443     shadow:'sides',
15444     /**
15445      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15446      * anchor positions (defaults to 'tl-bl')
15447      */
15448     listAlign: 'tl-bl?',
15449     /**
15450      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15451      */
15452     maxHeight: 300,
15453     /**
15454      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15455      * query specified by the allQuery config option (defaults to 'query')
15456      */
15457     triggerAction: 'query',
15458     /**
15459      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15460      * (defaults to 4, does not apply if editable = false)
15461      */
15462     minChars : 4,
15463     /**
15464      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15465      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15466      */
15467     typeAhead: false,
15468     /**
15469      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15470      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15471      */
15472     queryDelay: 500,
15473     /**
15474      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15475      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15476      */
15477     pageSize: 0,
15478     /**
15479      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15480      * when editable = true (defaults to false)
15481      */
15482     selectOnFocus:false,
15483     /**
15484      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15485      */
15486     queryParam: 'query',
15487     /**
15488      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15489      * when mode = 'remote' (defaults to 'Loading...')
15490      */
15491     loadingText: 'Loading...',
15492     /**
15493      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15494      */
15495     resizable: false,
15496     /**
15497      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15498      */
15499     handleHeight : 8,
15500     /**
15501      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15502      * traditional select (defaults to true)
15503      */
15504     editable: true,
15505     /**
15506      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15507      */
15508     allQuery: '',
15509     /**
15510      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15511      */
15512     mode: 'remote',
15513     /**
15514      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15515      * listWidth has a higher value)
15516      */
15517     minListWidth : 70,
15518     /**
15519      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15520      * allow the user to set arbitrary text into the field (defaults to false)
15521      */
15522     forceSelection:false,
15523     /**
15524      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15525      * if typeAhead = true (defaults to 250)
15526      */
15527     typeAheadDelay : 250,
15528     /**
15529      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15530      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15531      */
15532     valueNotFoundText : undefined,
15533     /**
15534      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15535      */
15536     blockFocus : false,
15537     
15538     /**
15539      * @cfg {Boolean} disableClear Disable showing of clear button.
15540      */
15541     disableClear : false,
15542     /**
15543      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15544      */
15545     alwaysQuery : false,
15546     
15547     /**
15548      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15549      */
15550     multiple : false,
15551     
15552     /**
15553      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15554      */
15555     invalidClass : "has-warning",
15556     
15557     /**
15558      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15559      */
15560     validClass : "has-success",
15561     
15562     /**
15563      * @cfg {Boolean} specialFilter (true|false) special filter default false
15564      */
15565     specialFilter : false,
15566     
15567     /**
15568      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15569      */
15570     mobileTouchView : true,
15571     
15572     /**
15573      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15574      */
15575     useNativeIOS : false,
15576     
15577     /**
15578      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15579      */
15580     mobile_restrict_height : false,
15581     
15582     ios_options : false,
15583     
15584     //private
15585     addicon : false,
15586     editicon: false,
15587     
15588     page: 0,
15589     hasQuery: false,
15590     append: false,
15591     loadNext: false,
15592     autoFocus : true,
15593     tickable : false,
15594     btnPosition : 'right',
15595     triggerList : true,
15596     showToggleBtn : true,
15597     animate : true,
15598     emptyResultText: 'Empty',
15599     triggerText : 'Select',
15600     emptyTitle : '',
15601     width : false,
15602     
15603     // element that contains real text value.. (when hidden is used..)
15604     
15605     getAutoCreate : function()
15606     {   
15607         var cfg = false;
15608         //render
15609         /*
15610          * Render classic select for iso
15611          */
15612         
15613         if(Roo.isIOS && this.useNativeIOS){
15614             cfg = this.getAutoCreateNativeIOS();
15615             return cfg;
15616         }
15617         
15618         /*
15619          * Touch Devices
15620          */
15621         
15622         if(Roo.isTouch && this.mobileTouchView){
15623             cfg = this.getAutoCreateTouchView();
15624             return cfg;;
15625         }
15626         
15627         /*
15628          *  Normal ComboBox
15629          */
15630         if(!this.tickable){
15631             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15632             return cfg;
15633         }
15634         
15635         /*
15636          *  ComboBox with tickable selections
15637          */
15638              
15639         var align = this.labelAlign || this.parentLabelAlign();
15640         
15641         cfg = {
15642             cls : 'form-group roo-combobox-tickable' //input-group
15643         };
15644         
15645         var btn_text_select = '';
15646         var btn_text_done = '';
15647         var btn_text_cancel = '';
15648         
15649         if (this.btn_text_show) {
15650             btn_text_select = 'Select';
15651             btn_text_done = 'Done';
15652             btn_text_cancel = 'Cancel'; 
15653         }
15654         
15655         var buttons = {
15656             tag : 'div',
15657             cls : 'tickable-buttons',
15658             cn : [
15659                 {
15660                     tag : 'button',
15661                     type : 'button',
15662                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15663                     //html : this.triggerText
15664                     html: btn_text_select
15665                 },
15666                 {
15667                     tag : 'button',
15668                     type : 'button',
15669                     name : 'ok',
15670                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15671                     //html : 'Done'
15672                     html: btn_text_done
15673                 },
15674                 {
15675                     tag : 'button',
15676                     type : 'button',
15677                     name : 'cancel',
15678                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15679                     //html : 'Cancel'
15680                     html: btn_text_cancel
15681                 }
15682             ]
15683         };
15684         
15685         if(this.editable){
15686             buttons.cn.unshift({
15687                 tag: 'input',
15688                 cls: 'roo-select2-search-field-input'
15689             });
15690         }
15691         
15692         var _this = this;
15693         
15694         Roo.each(buttons.cn, function(c){
15695             if (_this.size) {
15696                 c.cls += ' btn-' + _this.size;
15697             }
15698
15699             if (_this.disabled) {
15700                 c.disabled = true;
15701             }
15702         });
15703         
15704         var box = {
15705             tag: 'div',
15706             style : 'display: contents',
15707             cn: [
15708                 {
15709                     tag: 'input',
15710                     type : 'hidden',
15711                     cls: 'form-hidden-field'
15712                 },
15713                 {
15714                     tag: 'ul',
15715                     cls: 'roo-select2-choices',
15716                     cn:[
15717                         {
15718                             tag: 'li',
15719                             cls: 'roo-select2-search-field',
15720                             cn: [
15721                                 buttons
15722                             ]
15723                         }
15724                     ]
15725                 }
15726             ]
15727         };
15728         
15729         var combobox = {
15730             cls: 'roo-select2-container input-group roo-select2-container-multi',
15731             cn: [
15732                 
15733                 box
15734 //                {
15735 //                    tag: 'ul',
15736 //                    cls: 'typeahead typeahead-long dropdown-menu',
15737 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15738 //                }
15739             ]
15740         };
15741         
15742         if(this.hasFeedback && !this.allowBlank){
15743             
15744             var feedback = {
15745                 tag: 'span',
15746                 cls: 'glyphicon form-control-feedback'
15747             };
15748
15749             combobox.cn.push(feedback);
15750         }
15751         
15752         
15753         
15754         var indicator = {
15755             tag : 'i',
15756             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15757             tooltip : 'This field is required'
15758         };
15759         if (Roo.bootstrap.version == 4) {
15760             indicator = {
15761                 tag : 'i',
15762                 style : 'display:none'
15763             };
15764         }
15765         if (align ==='left' && this.fieldLabel.length) {
15766             
15767             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15768             
15769             cfg.cn = [
15770                 indicator,
15771                 {
15772                     tag: 'label',
15773                     'for' :  id,
15774                     cls : 'control-label col-form-label',
15775                     html : this.fieldLabel
15776
15777                 },
15778                 {
15779                     cls : "", 
15780                     cn: [
15781                         combobox
15782                     ]
15783                 }
15784
15785             ];
15786             
15787             var labelCfg = cfg.cn[1];
15788             var contentCfg = cfg.cn[2];
15789             
15790
15791             if(this.indicatorpos == 'right'){
15792                 
15793                 cfg.cn = [
15794                     {
15795                         tag: 'label',
15796                         'for' :  id,
15797                         cls : 'control-label col-form-label',
15798                         cn : [
15799                             {
15800                                 tag : 'span',
15801                                 html : this.fieldLabel
15802                             },
15803                             indicator
15804                         ]
15805                     },
15806                     {
15807                         cls : "",
15808                         cn: [
15809                             combobox
15810                         ]
15811                     }
15812
15813                 ];
15814                 
15815                 
15816                 
15817                 labelCfg = cfg.cn[0];
15818                 contentCfg = cfg.cn[1];
15819             
15820             }
15821             
15822             if(this.labelWidth > 12){
15823                 labelCfg.style = "width: " + this.labelWidth + 'px';
15824             }
15825             if(this.width * 1 > 0){
15826                 contentCfg.style = "width: " + this.width + 'px';
15827             }
15828             if(this.labelWidth < 13 && this.labelmd == 0){
15829                 this.labelmd = this.labelWidth;
15830             }
15831             
15832             if(this.labellg > 0){
15833                 labelCfg.cls += ' col-lg-' + this.labellg;
15834                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15835             }
15836             
15837             if(this.labelmd > 0){
15838                 labelCfg.cls += ' col-md-' + this.labelmd;
15839                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15840             }
15841             
15842             if(this.labelsm > 0){
15843                 labelCfg.cls += ' col-sm-' + this.labelsm;
15844                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15845             }
15846             
15847             if(this.labelxs > 0){
15848                 labelCfg.cls += ' col-xs-' + this.labelxs;
15849                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15850             }
15851                 
15852                 
15853         } else if ( this.fieldLabel.length) {
15854 //                Roo.log(" label");
15855                  cfg.cn = [
15856                    indicator,
15857                     {
15858                         tag: 'label',
15859                         //cls : 'input-group-addon',
15860                         html : this.fieldLabel
15861                     },
15862                     combobox
15863                 ];
15864                 
15865                 if(this.indicatorpos == 'right'){
15866                     cfg.cn = [
15867                         {
15868                             tag: 'label',
15869                             //cls : 'input-group-addon',
15870                             html : this.fieldLabel
15871                         },
15872                         indicator,
15873                         combobox
15874                     ];
15875                     
15876                 }
15877
15878         } else {
15879             
15880 //                Roo.log(" no label && no align");
15881                 cfg = combobox
15882                      
15883                 
15884         }
15885          
15886         var settings=this;
15887         ['xs','sm','md','lg'].map(function(size){
15888             if (settings[size]) {
15889                 cfg.cls += ' col-' + size + '-' + settings[size];
15890             }
15891         });
15892         
15893         return cfg;
15894         
15895     },
15896     
15897     _initEventsCalled : false,
15898     
15899     // private
15900     initEvents: function()
15901     {   
15902         if (this._initEventsCalled) { // as we call render... prevent looping...
15903             return;
15904         }
15905         this._initEventsCalled = true;
15906         
15907         if (!this.store) {
15908             throw "can not find store for combo";
15909         }
15910         
15911         this.indicator = this.indicatorEl();
15912         
15913         this.store = Roo.factory(this.store, Roo.data);
15914         this.store.parent = this;
15915         
15916         // if we are building from html. then this element is so complex, that we can not really
15917         // use the rendered HTML.
15918         // so we have to trash and replace the previous code.
15919         if (Roo.XComponent.build_from_html) {
15920             // remove this element....
15921             var e = this.el.dom, k=0;
15922             while (e ) { e = e.previousSibling;  ++k;}
15923
15924             this.el.remove();
15925             
15926             this.el=false;
15927             this.rendered = false;
15928             
15929             this.render(this.parent().getChildContainer(true), k);
15930         }
15931         
15932         if(Roo.isIOS && this.useNativeIOS){
15933             this.initIOSView();
15934             return;
15935         }
15936         
15937         /*
15938          * Touch Devices
15939          */
15940         
15941         if(Roo.isTouch && this.mobileTouchView){
15942             this.initTouchView();
15943             return;
15944         }
15945         
15946         if(this.tickable){
15947             this.initTickableEvents();
15948             return;
15949         }
15950         
15951         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15952         
15953         if(this.hiddenName){
15954             
15955             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15956             
15957             this.hiddenField.dom.value =
15958                 this.hiddenValue !== undefined ? this.hiddenValue :
15959                 this.value !== undefined ? this.value : '';
15960
15961             // prevent input submission
15962             this.el.dom.removeAttribute('name');
15963             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15964              
15965              
15966         }
15967         //if(Roo.isGecko){
15968         //    this.el.dom.setAttribute('autocomplete', 'off');
15969         //}
15970         
15971         var cls = 'x-combo-list';
15972         
15973         //this.list = new Roo.Layer({
15974         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15975         //});
15976         
15977         var _this = this;
15978         
15979         (function(){
15980             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15981             _this.list.setWidth(lw);
15982         }).defer(100);
15983         
15984         this.list.on('mouseover', this.onViewOver, this);
15985         this.list.on('mousemove', this.onViewMove, this);
15986         this.list.on('scroll', this.onViewScroll, this);
15987         
15988         /*
15989         this.list.swallowEvent('mousewheel');
15990         this.assetHeight = 0;
15991
15992         if(this.title){
15993             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15994             this.assetHeight += this.header.getHeight();
15995         }
15996
15997         this.innerList = this.list.createChild({cls:cls+'-inner'});
15998         this.innerList.on('mouseover', this.onViewOver, this);
15999         this.innerList.on('mousemove', this.onViewMove, this);
16000         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16001         
16002         if(this.allowBlank && !this.pageSize && !this.disableClear){
16003             this.footer = this.list.createChild({cls:cls+'-ft'});
16004             this.pageTb = new Roo.Toolbar(this.footer);
16005            
16006         }
16007         if(this.pageSize){
16008             this.footer = this.list.createChild({cls:cls+'-ft'});
16009             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16010                     {pageSize: this.pageSize});
16011             
16012         }
16013         
16014         if (this.pageTb && this.allowBlank && !this.disableClear) {
16015             var _this = this;
16016             this.pageTb.add(new Roo.Toolbar.Fill(), {
16017                 cls: 'x-btn-icon x-btn-clear',
16018                 text: '&#160;',
16019                 handler: function()
16020                 {
16021                     _this.collapse();
16022                     _this.clearValue();
16023                     _this.onSelect(false, -1);
16024                 }
16025             });
16026         }
16027         if (this.footer) {
16028             this.assetHeight += this.footer.getHeight();
16029         }
16030         */
16031             
16032         if(!this.tpl){
16033             this.tpl = Roo.bootstrap.version == 4 ?
16034                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16035                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16036         }
16037
16038         this.view = new Roo.View(this.list, this.tpl, {
16039             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16040         });
16041         //this.view.wrapEl.setDisplayed(false);
16042         this.view.on('click', this.onViewClick, this);
16043         
16044         
16045         this.store.on('beforeload', this.onBeforeLoad, this);
16046         this.store.on('load', this.onLoad, this);
16047         this.store.on('loadexception', this.onLoadException, this);
16048         /*
16049         if(this.resizable){
16050             this.resizer = new Roo.Resizable(this.list,  {
16051                pinned:true, handles:'se'
16052             });
16053             this.resizer.on('resize', function(r, w, h){
16054                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16055                 this.listWidth = w;
16056                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16057                 this.restrictHeight();
16058             }, this);
16059             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16060         }
16061         */
16062         if(!this.editable){
16063             this.editable = true;
16064             this.setEditable(false);
16065         }
16066         
16067         /*
16068         
16069         if (typeof(this.events.add.listeners) != 'undefined') {
16070             
16071             this.addicon = this.wrap.createChild(
16072                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16073        
16074             this.addicon.on('click', function(e) {
16075                 this.fireEvent('add', this);
16076             }, this);
16077         }
16078         if (typeof(this.events.edit.listeners) != 'undefined') {
16079             
16080             this.editicon = this.wrap.createChild(
16081                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16082             if (this.addicon) {
16083                 this.editicon.setStyle('margin-left', '40px');
16084             }
16085             this.editicon.on('click', function(e) {
16086                 
16087                 // we fire even  if inothing is selected..
16088                 this.fireEvent('edit', this, this.lastData );
16089                 
16090             }, this);
16091         }
16092         */
16093         
16094         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16095             "up" : function(e){
16096                 this.inKeyMode = true;
16097                 this.selectPrev();
16098             },
16099
16100             "down" : function(e){
16101                 if(!this.isExpanded()){
16102                     this.onTriggerClick();
16103                 }else{
16104                     this.inKeyMode = true;
16105                     this.selectNext();
16106                 }
16107             },
16108
16109             "enter" : function(e){
16110 //                this.onViewClick();
16111                 //return true;
16112                 this.collapse();
16113                 
16114                 if(this.fireEvent("specialkey", this, e)){
16115                     this.onViewClick(false);
16116                 }
16117                 
16118                 return true;
16119             },
16120
16121             "esc" : function(e){
16122                 this.collapse();
16123             },
16124
16125             "tab" : function(e){
16126                 this.collapse();
16127                 
16128                 if(this.fireEvent("specialkey", this, e)){
16129                     this.onViewClick(false);
16130                 }
16131                 
16132                 return true;
16133             },
16134
16135             scope : this,
16136
16137             doRelay : function(foo, bar, hname){
16138                 if(hname == 'down' || this.scope.isExpanded()){
16139                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16140                 }
16141                 return true;
16142             },
16143
16144             forceKeyDown: true
16145         });
16146         
16147         
16148         this.queryDelay = Math.max(this.queryDelay || 10,
16149                 this.mode == 'local' ? 10 : 250);
16150         
16151         
16152         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16153         
16154         if(this.typeAhead){
16155             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16156         }
16157         if(this.editable !== false){
16158             this.inputEl().on("keyup", this.onKeyUp, this);
16159         }
16160         if(this.forceSelection){
16161             this.inputEl().on('blur', this.doForce, this);
16162         }
16163         
16164         if(this.multiple){
16165             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16166             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16167         }
16168     },
16169     
16170     initTickableEvents: function()
16171     {   
16172         this.createList();
16173         
16174         if(this.hiddenName){
16175             
16176             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16177             
16178             this.hiddenField.dom.value =
16179                 this.hiddenValue !== undefined ? this.hiddenValue :
16180                 this.value !== undefined ? this.value : '';
16181
16182             // prevent input submission
16183             this.el.dom.removeAttribute('name');
16184             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16185              
16186              
16187         }
16188         
16189 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16190         
16191         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16192         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16193         if(this.triggerList){
16194             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16195         }
16196          
16197         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16198         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16199         
16200         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16201         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16202         
16203         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16204         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16205         
16206         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16207         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16209         
16210         this.okBtn.hide();
16211         this.cancelBtn.hide();
16212         
16213         var _this = this;
16214         
16215         (function(){
16216             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16217             _this.list.setWidth(lw);
16218         }).defer(100);
16219         
16220         this.list.on('mouseover', this.onViewOver, this);
16221         this.list.on('mousemove', this.onViewMove, this);
16222         
16223         this.list.on('scroll', this.onViewScroll, this);
16224         
16225         if(!this.tpl){
16226             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16227                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16228         }
16229
16230         this.view = new Roo.View(this.list, this.tpl, {
16231             singleSelect:true,
16232             tickable:true,
16233             parent:this,
16234             store: this.store,
16235             selectedClass: this.selectedClass
16236         });
16237         
16238         //this.view.wrapEl.setDisplayed(false);
16239         this.view.on('click', this.onViewClick, this);
16240         
16241         
16242         
16243         this.store.on('beforeload', this.onBeforeLoad, this);
16244         this.store.on('load', this.onLoad, this);
16245         this.store.on('loadexception', this.onLoadException, this);
16246         
16247         if(this.editable){
16248             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16249                 "up" : function(e){
16250                     this.inKeyMode = true;
16251                     this.selectPrev();
16252                 },
16253
16254                 "down" : function(e){
16255                     this.inKeyMode = true;
16256                     this.selectNext();
16257                 },
16258
16259                 "enter" : function(e){
16260                     if(this.fireEvent("specialkey", this, e)){
16261                         this.onViewClick(false);
16262                     }
16263                     
16264                     return true;
16265                 },
16266
16267                 "esc" : function(e){
16268                     this.onTickableFooterButtonClick(e, false, false);
16269                 },
16270
16271                 "tab" : function(e){
16272                     this.fireEvent("specialkey", this, e);
16273                     
16274                     this.onTickableFooterButtonClick(e, false, false);
16275                     
16276                     return true;
16277                 },
16278
16279                 scope : this,
16280
16281                 doRelay : function(e, fn, key){
16282                     if(this.scope.isExpanded()){
16283                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16284                     }
16285                     return true;
16286                 },
16287
16288                 forceKeyDown: true
16289             });
16290         }
16291         
16292         this.queryDelay = Math.max(this.queryDelay || 10,
16293                 this.mode == 'local' ? 10 : 250);
16294         
16295         
16296         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16297         
16298         if(this.typeAhead){
16299             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16300         }
16301         
16302         if(this.editable !== false){
16303             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16304         }
16305         
16306         this.indicator = this.indicatorEl();
16307         
16308         if(this.indicator){
16309             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16310             this.indicator.hide();
16311         }
16312         
16313     },
16314
16315     onDestroy : function(){
16316         if(this.view){
16317             this.view.setStore(null);
16318             this.view.el.removeAllListeners();
16319             this.view.el.remove();
16320             this.view.purgeListeners();
16321         }
16322         if(this.list){
16323             this.list.dom.innerHTML  = '';
16324         }
16325         
16326         if(this.store){
16327             this.store.un('beforeload', this.onBeforeLoad, this);
16328             this.store.un('load', this.onLoad, this);
16329             this.store.un('loadexception', this.onLoadException, this);
16330         }
16331         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16332     },
16333
16334     // private
16335     fireKey : function(e){
16336         if(e.isNavKeyPress() && !this.list.isVisible()){
16337             this.fireEvent("specialkey", this, e);
16338         }
16339     },
16340
16341     // private
16342     onResize: function(w, h)
16343     {
16344         
16345         
16346 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16347 //        
16348 //        if(typeof w != 'number'){
16349 //            // we do not handle it!?!?
16350 //            return;
16351 //        }
16352 //        var tw = this.trigger.getWidth();
16353 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16354 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16355 //        var x = w - tw;
16356 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16357 //            
16358 //        //this.trigger.setStyle('left', x+'px');
16359 //        
16360 //        if(this.list && this.listWidth === undefined){
16361 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16362 //            this.list.setWidth(lw);
16363 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16364 //        }
16365         
16366     
16367         
16368     },
16369
16370     /**
16371      * Allow or prevent the user from directly editing the field text.  If false is passed,
16372      * the user will only be able to select from the items defined in the dropdown list.  This method
16373      * is the runtime equivalent of setting the 'editable' config option at config time.
16374      * @param {Boolean} value True to allow the user to directly edit the field text
16375      */
16376     setEditable : function(value){
16377         if(value == this.editable){
16378             return;
16379         }
16380         this.editable = value;
16381         if(!value){
16382             this.inputEl().dom.setAttribute('readOnly', true);
16383             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16384             this.inputEl().addClass('x-combo-noedit');
16385         }else{
16386             this.inputEl().dom.removeAttribute('readOnly');
16387             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16388             this.inputEl().removeClass('x-combo-noedit');
16389         }
16390     },
16391
16392     // private
16393     
16394     onBeforeLoad : function(combo,opts){
16395         if(!this.hasFocus){
16396             return;
16397         }
16398          if (!opts.add) {
16399             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16400          }
16401         this.restrictHeight();
16402         this.selectedIndex = -1;
16403     },
16404
16405     // private
16406     onLoad : function(){
16407         
16408         this.hasQuery = false;
16409         
16410         if(!this.hasFocus){
16411             return;
16412         }
16413         
16414         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16415             this.loading.hide();
16416         }
16417         
16418         if(this.store.getCount() > 0){
16419             
16420             this.expand();
16421             this.restrictHeight();
16422             if(this.lastQuery == this.allQuery){
16423                 if(this.editable && !this.tickable){
16424                     this.inputEl().dom.select();
16425                 }
16426                 
16427                 if(
16428                     !this.selectByValue(this.value, true) &&
16429                     this.autoFocus && 
16430                     (
16431                         !this.store.lastOptions ||
16432                         typeof(this.store.lastOptions.add) == 'undefined' || 
16433                         this.store.lastOptions.add != true
16434                     )
16435                 ){
16436                     this.select(0, true);
16437                 }
16438             }else{
16439                 if(this.autoFocus){
16440                     this.selectNext();
16441                 }
16442                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16443                     this.taTask.delay(this.typeAheadDelay);
16444                 }
16445             }
16446         }else{
16447             this.onEmptyResults();
16448         }
16449         
16450         //this.el.focus();
16451     },
16452     // private
16453     onLoadException : function()
16454     {
16455         this.hasQuery = false;
16456         
16457         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16458             this.loading.hide();
16459         }
16460         
16461         if(this.tickable && this.editable){
16462             return;
16463         }
16464         
16465         this.collapse();
16466         // only causes errors at present
16467         //Roo.log(this.store.reader.jsonData);
16468         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16469             // fixme
16470             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16471         //}
16472         
16473         
16474     },
16475     // private
16476     onTypeAhead : function(){
16477         if(this.store.getCount() > 0){
16478             var r = this.store.getAt(0);
16479             var newValue = r.data[this.displayField];
16480             var len = newValue.length;
16481             var selStart = this.getRawValue().length;
16482             
16483             if(selStart != len){
16484                 this.setRawValue(newValue);
16485                 this.selectText(selStart, newValue.length);
16486             }
16487         }
16488     },
16489
16490     // private
16491     onSelect : function(record, index){
16492         
16493         if(this.fireEvent('beforeselect', this, record, index) !== false){
16494         
16495             this.setFromData(index > -1 ? record.data : false);
16496             
16497             this.collapse();
16498             this.fireEvent('select', this, record, index);
16499         }
16500     },
16501
16502     /**
16503      * Returns the currently selected field value or empty string if no value is set.
16504      * @return {String} value The selected value
16505      */
16506     getValue : function()
16507     {
16508         if(Roo.isIOS && this.useNativeIOS){
16509             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16510         }
16511         
16512         if(this.multiple){
16513             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16514         }
16515         
16516         if(this.valueField){
16517             return typeof this.value != 'undefined' ? this.value : '';
16518         }else{
16519             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16520         }
16521     },
16522     
16523     getRawValue : function()
16524     {
16525         if(Roo.isIOS && this.useNativeIOS){
16526             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16527         }
16528         
16529         var v = this.inputEl().getValue();
16530         
16531         return v;
16532     },
16533
16534     /**
16535      * Clears any text/value currently set in the field
16536      */
16537     clearValue : function(){
16538         
16539         if(this.hiddenField){
16540             this.hiddenField.dom.value = '';
16541         }
16542         this.value = '';
16543         this.setRawValue('');
16544         this.lastSelectionText = '';
16545         this.lastData = false;
16546         
16547         var close = this.closeTriggerEl();
16548         
16549         if(close){
16550             close.hide();
16551         }
16552         
16553         this.validate();
16554         
16555     },
16556
16557     /**
16558      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16559      * will be displayed in the field.  If the value does not match the data value of an existing item,
16560      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16561      * Otherwise the field will be blank (although the value will still be set).
16562      * @param {String} value The value to match
16563      */
16564     setValue : function(v)
16565     {
16566         if(Roo.isIOS && this.useNativeIOS){
16567             this.setIOSValue(v);
16568             return;
16569         }
16570         
16571         if(this.multiple){
16572             this.syncValue();
16573             return;
16574         }
16575         
16576         var text = v;
16577         if(this.valueField){
16578             var r = this.findRecord(this.valueField, v);
16579             if(r){
16580                 text = r.data[this.displayField];
16581             }else if(this.valueNotFoundText !== undefined){
16582                 text = this.valueNotFoundText;
16583             }
16584         }
16585         this.lastSelectionText = text;
16586         if(this.hiddenField){
16587             this.hiddenField.dom.value = v;
16588         }
16589         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16590         this.value = v;
16591         
16592         var close = this.closeTriggerEl();
16593         
16594         if(close){
16595             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16596         }
16597         
16598         this.validate();
16599     },
16600     /**
16601      * @property {Object} the last set data for the element
16602      */
16603     
16604     lastData : false,
16605     /**
16606      * Sets the value of the field based on a object which is related to the record format for the store.
16607      * @param {Object} value the value to set as. or false on reset?
16608      */
16609     setFromData : function(o){
16610         
16611         if(this.multiple){
16612             this.addItem(o);
16613             return;
16614         }
16615             
16616         var dv = ''; // display value
16617         var vv = ''; // value value..
16618         this.lastData = o;
16619         if (this.displayField) {
16620             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16621         } else {
16622             // this is an error condition!!!
16623             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16624         }
16625         
16626         if(this.valueField){
16627             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16628         }
16629         
16630         var close = this.closeTriggerEl();
16631         
16632         if(close){
16633             if(dv.length || vv * 1 > 0){
16634                 close.show() ;
16635                 this.blockFocus=true;
16636             } else {
16637                 close.hide();
16638             }             
16639         }
16640         
16641         if(this.hiddenField){
16642             this.hiddenField.dom.value = vv;
16643             
16644             this.lastSelectionText = dv;
16645             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16646             this.value = vv;
16647             return;
16648         }
16649         // no hidden field.. - we store the value in 'value', but still display
16650         // display field!!!!
16651         this.lastSelectionText = dv;
16652         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16653         this.value = vv;
16654         
16655         
16656         
16657     },
16658     // private
16659     reset : function(){
16660         // overridden so that last data is reset..
16661         
16662         if(this.multiple){
16663             this.clearItem();
16664             return;
16665         }
16666         
16667         this.setValue(this.originalValue);
16668         //this.clearInvalid();
16669         this.lastData = false;
16670         if (this.view) {
16671             this.view.clearSelections();
16672         }
16673         
16674         this.validate();
16675     },
16676     // private
16677     findRecord : function(prop, value){
16678         var record;
16679         if(this.store.getCount() > 0){
16680             this.store.each(function(r){
16681                 if(r.data[prop] == value){
16682                     record = r;
16683                     return false;
16684                 }
16685                 return true;
16686             });
16687         }
16688         return record;
16689     },
16690     
16691     getName: function()
16692     {
16693         // returns hidden if it's set..
16694         if (!this.rendered) {return ''};
16695         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16696         
16697     },
16698     // private
16699     onViewMove : function(e, t){
16700         this.inKeyMode = false;
16701     },
16702
16703     // private
16704     onViewOver : function(e, t){
16705         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16706             return;
16707         }
16708         var item = this.view.findItemFromChild(t);
16709         
16710         if(item){
16711             var index = this.view.indexOf(item);
16712             this.select(index, false);
16713         }
16714     },
16715
16716     // private
16717     onViewClick : function(view, doFocus, el, e)
16718     {
16719         var index = this.view.getSelectedIndexes()[0];
16720         
16721         var r = this.store.getAt(index);
16722         
16723         if(this.tickable){
16724             
16725             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16726                 return;
16727             }
16728             
16729             var rm = false;
16730             var _this = this;
16731             
16732             Roo.each(this.tickItems, function(v,k){
16733                 
16734                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16735                     Roo.log(v);
16736                     _this.tickItems.splice(k, 1);
16737                     
16738                     if(typeof(e) == 'undefined' && view == false){
16739                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16740                     }
16741                     
16742                     rm = true;
16743                     return;
16744                 }
16745             });
16746             
16747             if(rm){
16748                 return;
16749             }
16750             
16751             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16752                 this.tickItems.push(r.data);
16753             }
16754             
16755             if(typeof(e) == 'undefined' && view == false){
16756                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16757             }
16758                     
16759             return;
16760         }
16761         
16762         if(r){
16763             this.onSelect(r, index);
16764         }
16765         if(doFocus !== false && !this.blockFocus){
16766             this.inputEl().focus();
16767         }
16768     },
16769
16770     // private
16771     restrictHeight : function(){
16772         //this.innerList.dom.style.height = '';
16773         //var inner = this.innerList.dom;
16774         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16775         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16776         //this.list.beginUpdate();
16777         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16778         this.list.alignTo(this.inputEl(), this.listAlign);
16779         this.list.alignTo(this.inputEl(), this.listAlign);
16780         //this.list.endUpdate();
16781     },
16782
16783     // private
16784     onEmptyResults : function(){
16785         
16786         if(this.tickable && this.editable){
16787             this.hasFocus = false;
16788             this.restrictHeight();
16789             return;
16790         }
16791         
16792         this.collapse();
16793     },
16794
16795     /**
16796      * Returns true if the dropdown list is expanded, else false.
16797      */
16798     isExpanded : function(){
16799         return this.list.isVisible();
16800     },
16801
16802     /**
16803      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16804      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16805      * @param {String} value The data value of the item to select
16806      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16807      * selected item if it is not currently in view (defaults to true)
16808      * @return {Boolean} True if the value matched an item in the list, else false
16809      */
16810     selectByValue : function(v, scrollIntoView){
16811         if(v !== undefined && v !== null){
16812             var r = this.findRecord(this.valueField || this.displayField, v);
16813             if(r){
16814                 this.select(this.store.indexOf(r), scrollIntoView);
16815                 return true;
16816             }
16817         }
16818         return false;
16819     },
16820
16821     /**
16822      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16823      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824      * @param {Number} index The zero-based index of the list item to select
16825      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826      * selected item if it is not currently in view (defaults to true)
16827      */
16828     select : function(index, scrollIntoView){
16829         this.selectedIndex = index;
16830         this.view.select(index);
16831         if(scrollIntoView !== false){
16832             var el = this.view.getNode(index);
16833             /*
16834              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16835              */
16836             if(el){
16837                 this.list.scrollChildIntoView(el, false);
16838             }
16839         }
16840     },
16841
16842     // private
16843     selectNext : function(){
16844         var ct = this.store.getCount();
16845         if(ct > 0){
16846             if(this.selectedIndex == -1){
16847                 this.select(0);
16848             }else if(this.selectedIndex < ct-1){
16849                 this.select(this.selectedIndex+1);
16850             }
16851         }
16852     },
16853
16854     // private
16855     selectPrev : function(){
16856         var ct = this.store.getCount();
16857         if(ct > 0){
16858             if(this.selectedIndex == -1){
16859                 this.select(0);
16860             }else if(this.selectedIndex != 0){
16861                 this.select(this.selectedIndex-1);
16862             }
16863         }
16864     },
16865
16866     // private
16867     onKeyUp : function(e){
16868         if(this.editable !== false && !e.isSpecialKey()){
16869             this.lastKey = e.getKey();
16870             this.dqTask.delay(this.queryDelay);
16871         }
16872     },
16873
16874     // private
16875     validateBlur : function(){
16876         return !this.list || !this.list.isVisible();   
16877     },
16878
16879     // private
16880     initQuery : function(){
16881         
16882         var v = this.getRawValue();
16883         
16884         if(this.tickable && this.editable){
16885             v = this.tickableInputEl().getValue();
16886         }
16887         
16888         this.doQuery(v);
16889     },
16890
16891     // private
16892     doForce : function(){
16893         if(this.inputEl().dom.value.length > 0){
16894             this.inputEl().dom.value =
16895                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16896              
16897         }
16898     },
16899
16900     /**
16901      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16902      * query allowing the query action to be canceled if needed.
16903      * @param {String} query The SQL query to execute
16904      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16905      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16906      * saved in the current store (defaults to false)
16907      */
16908     doQuery : function(q, forceAll){
16909         
16910         if(q === undefined || q === null){
16911             q = '';
16912         }
16913         var qe = {
16914             query: q,
16915             forceAll: forceAll,
16916             combo: this,
16917             cancel:false
16918         };
16919         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16920             return false;
16921         }
16922         q = qe.query;
16923         
16924         forceAll = qe.forceAll;
16925         if(forceAll === true || (q.length >= this.minChars)){
16926             
16927             this.hasQuery = true;
16928             
16929             if(this.lastQuery != q || this.alwaysQuery){
16930                 this.lastQuery = q;
16931                 if(this.mode == 'local'){
16932                     this.selectedIndex = -1;
16933                     if(forceAll){
16934                         this.store.clearFilter();
16935                     }else{
16936                         
16937                         if(this.specialFilter){
16938                             this.fireEvent('specialfilter', this);
16939                             this.onLoad();
16940                             return;
16941                         }
16942                         
16943                         this.store.filter(this.displayField, q);
16944                     }
16945                     
16946                     this.store.fireEvent("datachanged", this.store);
16947                     
16948                     this.onLoad();
16949                     
16950                     
16951                 }else{
16952                     
16953                     this.store.baseParams[this.queryParam] = q;
16954                     
16955                     var options = {params : this.getParams(q)};
16956                     
16957                     if(this.loadNext){
16958                         options.add = true;
16959                         options.params.start = this.page * this.pageSize;
16960                     }
16961                     
16962                     this.store.load(options);
16963                     
16964                     /*
16965                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16966                      *  we should expand the list on onLoad
16967                      *  so command out it
16968                      */
16969 //                    this.expand();
16970                 }
16971             }else{
16972                 this.selectedIndex = -1;
16973                 this.onLoad();   
16974             }
16975         }
16976         
16977         this.loadNext = false;
16978     },
16979     
16980     // private
16981     getParams : function(q){
16982         var p = {};
16983         //p[this.queryParam] = q;
16984         
16985         if(this.pageSize){
16986             p.start = 0;
16987             p.limit = this.pageSize;
16988         }
16989         return p;
16990     },
16991
16992     /**
16993      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16994      */
16995     collapse : function(){
16996         if(!this.isExpanded()){
16997             return;
16998         }
16999         
17000         this.list.hide();
17001         
17002         this.hasFocus = false;
17003         
17004         if(this.tickable){
17005             this.okBtn.hide();
17006             this.cancelBtn.hide();
17007             this.trigger.show();
17008             
17009             if(this.editable){
17010                 this.tickableInputEl().dom.value = '';
17011                 this.tickableInputEl().blur();
17012             }
17013             
17014         }
17015         
17016         Roo.get(document).un('mousedown', this.collapseIf, this);
17017         Roo.get(document).un('mousewheel', this.collapseIf, this);
17018         if (!this.editable) {
17019             Roo.get(document).un('keydown', this.listKeyPress, this);
17020         }
17021         this.fireEvent('collapse', this);
17022         
17023         this.validate();
17024     },
17025
17026     // private
17027     collapseIf : function(e){
17028         var in_combo  = e.within(this.el);
17029         var in_list =  e.within(this.list);
17030         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17031         
17032         if (in_combo || in_list || is_list) {
17033             //e.stopPropagation();
17034             return;
17035         }
17036         
17037         if(this.tickable){
17038             this.onTickableFooterButtonClick(e, false, false);
17039         }
17040
17041         this.collapse();
17042         
17043     },
17044
17045     /**
17046      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17047      */
17048     expand : function(){
17049        
17050         if(this.isExpanded() || !this.hasFocus){
17051             return;
17052         }
17053         
17054         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17055         this.list.setWidth(lw);
17056         
17057         Roo.log('expand');
17058         
17059         this.list.show();
17060         
17061         this.restrictHeight();
17062         
17063         if(this.tickable){
17064             
17065             this.tickItems = Roo.apply([], this.item);
17066             
17067             this.okBtn.show();
17068             this.cancelBtn.show();
17069             this.trigger.hide();
17070             
17071             if(this.editable){
17072                 this.tickableInputEl().focus();
17073             }
17074             
17075         }
17076         
17077         Roo.get(document).on('mousedown', this.collapseIf, this);
17078         Roo.get(document).on('mousewheel', this.collapseIf, this);
17079         if (!this.editable) {
17080             Roo.get(document).on('keydown', this.listKeyPress, this);
17081         }
17082         
17083         this.fireEvent('expand', this);
17084     },
17085
17086     // private
17087     // Implements the default empty TriggerField.onTriggerClick function
17088     onTriggerClick : function(e)
17089     {
17090         Roo.log('trigger click');
17091         
17092         if(this.disabled || !this.triggerList){
17093             return;
17094         }
17095         
17096         this.page = 0;
17097         this.loadNext = false;
17098         
17099         if(this.isExpanded()){
17100             this.collapse();
17101             if (!this.blockFocus) {
17102                 this.inputEl().focus();
17103             }
17104             
17105         }else {
17106             this.hasFocus = true;
17107             if(this.triggerAction == 'all') {
17108                 this.doQuery(this.allQuery, true);
17109             } else {
17110                 this.doQuery(this.getRawValue());
17111             }
17112             if (!this.blockFocus) {
17113                 this.inputEl().focus();
17114             }
17115         }
17116     },
17117     
17118     onTickableTriggerClick : function(e)
17119     {
17120         if(this.disabled){
17121             return;
17122         }
17123         
17124         this.page = 0;
17125         this.loadNext = false;
17126         this.hasFocus = true;
17127         
17128         if(this.triggerAction == 'all') {
17129             this.doQuery(this.allQuery, true);
17130         } else {
17131             this.doQuery(this.getRawValue());
17132         }
17133     },
17134     
17135     onSearchFieldClick : function(e)
17136     {
17137         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17138             this.onTickableFooterButtonClick(e, false, false);
17139             return;
17140         }
17141         
17142         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17143             return;
17144         }
17145         
17146         this.page = 0;
17147         this.loadNext = false;
17148         this.hasFocus = true;
17149         
17150         if(this.triggerAction == 'all') {
17151             this.doQuery(this.allQuery, true);
17152         } else {
17153             this.doQuery(this.getRawValue());
17154         }
17155     },
17156     
17157     listKeyPress : function(e)
17158     {
17159         //Roo.log('listkeypress');
17160         // scroll to first matching element based on key pres..
17161         if (e.isSpecialKey()) {
17162             return false;
17163         }
17164         var k = String.fromCharCode(e.getKey()).toUpperCase();
17165         //Roo.log(k);
17166         var match  = false;
17167         var csel = this.view.getSelectedNodes();
17168         var cselitem = false;
17169         if (csel.length) {
17170             var ix = this.view.indexOf(csel[0]);
17171             cselitem  = this.store.getAt(ix);
17172             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17173                 cselitem = false;
17174             }
17175             
17176         }
17177         
17178         this.store.each(function(v) { 
17179             if (cselitem) {
17180                 // start at existing selection.
17181                 if (cselitem.id == v.id) {
17182                     cselitem = false;
17183                 }
17184                 return true;
17185             }
17186                 
17187             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17188                 match = this.store.indexOf(v);
17189                 return false;
17190             }
17191             return true;
17192         }, this);
17193         
17194         if (match === false) {
17195             return true; // no more action?
17196         }
17197         // scroll to?
17198         this.view.select(match);
17199         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17200         sn.scrollIntoView(sn.dom.parentNode, false);
17201     },
17202     
17203     onViewScroll : function(e, t){
17204         
17205         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){
17206             return;
17207         }
17208         
17209         this.hasQuery = true;
17210         
17211         this.loading = this.list.select('.loading', true).first();
17212         
17213         if(this.loading === null){
17214             this.list.createChild({
17215                 tag: 'div',
17216                 cls: 'loading roo-select2-more-results roo-select2-active',
17217                 html: 'Loading more results...'
17218             });
17219             
17220             this.loading = this.list.select('.loading', true).first();
17221             
17222             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17223             
17224             this.loading.hide();
17225         }
17226         
17227         this.loading.show();
17228         
17229         var _combo = this;
17230         
17231         this.page++;
17232         this.loadNext = true;
17233         
17234         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17235         
17236         return;
17237     },
17238     
17239     addItem : function(o)
17240     {   
17241         var dv = ''; // display value
17242         
17243         if (this.displayField) {
17244             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17245         } else {
17246             // this is an error condition!!!
17247             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17248         }
17249         
17250         if(!dv.length){
17251             return;
17252         }
17253         
17254         var choice = this.choices.createChild({
17255             tag: 'li',
17256             cls: 'roo-select2-search-choice',
17257             cn: [
17258                 {
17259                     tag: 'div',
17260                     html: dv
17261                 },
17262                 {
17263                     tag: 'a',
17264                     href: '#',
17265                     cls: 'roo-select2-search-choice-close fa fa-times',
17266                     tabindex: '-1'
17267                 }
17268             ]
17269             
17270         }, this.searchField);
17271         
17272         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17273         
17274         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17275         
17276         this.item.push(o);
17277         
17278         this.lastData = o;
17279         
17280         this.syncValue();
17281         
17282         this.inputEl().dom.value = '';
17283         
17284         this.validate();
17285     },
17286     
17287     onRemoveItem : function(e, _self, o)
17288     {
17289         e.preventDefault();
17290         
17291         this.lastItem = Roo.apply([], this.item);
17292         
17293         var index = this.item.indexOf(o.data) * 1;
17294         
17295         if( index < 0){
17296             Roo.log('not this item?!');
17297             return;
17298         }
17299         
17300         this.item.splice(index, 1);
17301         o.item.remove();
17302         
17303         this.syncValue();
17304         
17305         this.fireEvent('remove', this, e);
17306         
17307         this.validate();
17308         
17309     },
17310     
17311     syncValue : function()
17312     {
17313         if(!this.item.length){
17314             this.clearValue();
17315             return;
17316         }
17317             
17318         var value = [];
17319         var _this = this;
17320         Roo.each(this.item, function(i){
17321             if(_this.valueField){
17322                 value.push(i[_this.valueField]);
17323                 return;
17324             }
17325
17326             value.push(i);
17327         });
17328
17329         this.value = value.join(',');
17330
17331         if(this.hiddenField){
17332             this.hiddenField.dom.value = this.value;
17333         }
17334         
17335         this.store.fireEvent("datachanged", this.store);
17336         
17337         this.validate();
17338     },
17339     
17340     clearItem : function()
17341     {
17342         if(!this.multiple){
17343             return;
17344         }
17345         
17346         this.item = [];
17347         
17348         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17349            c.remove();
17350         });
17351         
17352         this.syncValue();
17353         
17354         this.validate();
17355         
17356         if(this.tickable && !Roo.isTouch){
17357             this.view.refresh();
17358         }
17359     },
17360     
17361     inputEl: function ()
17362     {
17363         if(Roo.isIOS && this.useNativeIOS){
17364             return this.el.select('select.roo-ios-select', true).first();
17365         }
17366         
17367         if(Roo.isTouch && this.mobileTouchView){
17368             return this.el.select('input.form-control',true).first();
17369         }
17370         
17371         if(this.tickable){
17372             return this.searchField;
17373         }
17374         
17375         return this.el.select('input.form-control',true).first();
17376     },
17377     
17378     onTickableFooterButtonClick : function(e, btn, el)
17379     {
17380         e.preventDefault();
17381         
17382         this.lastItem = Roo.apply([], this.item);
17383         
17384         if(btn && btn.name == 'cancel'){
17385             this.tickItems = Roo.apply([], this.item);
17386             this.collapse();
17387             return;
17388         }
17389         
17390         this.clearItem();
17391         
17392         var _this = this;
17393         
17394         Roo.each(this.tickItems, function(o){
17395             _this.addItem(o);
17396         });
17397         
17398         this.collapse();
17399         
17400     },
17401     
17402     validate : function()
17403     {
17404         if(this.getVisibilityEl().hasClass('hidden')){
17405             return true;
17406         }
17407         
17408         var v = this.getRawValue();
17409         
17410         if(this.multiple){
17411             v = this.getValue();
17412         }
17413         
17414         if(this.disabled || this.allowBlank || v.length){
17415             this.markValid();
17416             return true;
17417         }
17418         
17419         this.markInvalid();
17420         return false;
17421     },
17422     
17423     tickableInputEl : function()
17424     {
17425         if(!this.tickable || !this.editable){
17426             return this.inputEl();
17427         }
17428         
17429         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17430     },
17431     
17432     
17433     getAutoCreateTouchView : function()
17434     {
17435         var id = Roo.id();
17436         
17437         var cfg = {
17438             cls: 'form-group' //input-group
17439         };
17440         
17441         var input =  {
17442             tag: 'input',
17443             id : id,
17444             type : this.inputType,
17445             cls : 'form-control x-combo-noedit',
17446             autocomplete: 'new-password',
17447             placeholder : this.placeholder || '',
17448             readonly : true
17449         };
17450         
17451         if (this.name) {
17452             input.name = this.name;
17453         }
17454         
17455         if (this.size) {
17456             input.cls += ' input-' + this.size;
17457         }
17458         
17459         if (this.disabled) {
17460             input.disabled = true;
17461         }
17462         
17463         var inputblock = {
17464             cls : 'roo-combobox-wrap',
17465             cn : [
17466                 input
17467             ]
17468         };
17469         
17470         if(this.before){
17471             inputblock.cls += ' input-group';
17472             
17473             inputblock.cn.unshift({
17474                 tag :'span',
17475                 cls : 'input-group-addon input-group-prepend input-group-text',
17476                 html : this.before
17477             });
17478         }
17479         
17480         if(this.removable && !this.multiple){
17481             inputblock.cls += ' roo-removable';
17482             
17483             inputblock.cn.push({
17484                 tag: 'button',
17485                 html : 'x',
17486                 cls : 'roo-combo-removable-btn close'
17487             });
17488         }
17489
17490         if(this.hasFeedback && !this.allowBlank){
17491             
17492             inputblock.cls += ' has-feedback';
17493             
17494             inputblock.cn.push({
17495                 tag: 'span',
17496                 cls: 'glyphicon form-control-feedback'
17497             });
17498             
17499         }
17500         
17501         if (this.after) {
17502             
17503             inputblock.cls += (this.before) ? '' : ' input-group';
17504             
17505             inputblock.cn.push({
17506                 tag :'span',
17507                 cls : 'input-group-addon input-group-append input-group-text',
17508                 html : this.after
17509             });
17510         }
17511
17512         
17513         var ibwrap = inputblock;
17514         
17515         if(this.multiple){
17516             ibwrap = {
17517                 tag: 'ul',
17518                 cls: 'roo-select2-choices',
17519                 cn:[
17520                     {
17521                         tag: 'li',
17522                         cls: 'roo-select2-search-field',
17523                         cn: [
17524
17525                             inputblock
17526                         ]
17527                     }
17528                 ]
17529             };
17530         
17531             
17532         }
17533         
17534         var combobox = {
17535             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17536             cn: [
17537                 {
17538                     tag: 'input',
17539                     type : 'hidden',
17540                     cls: 'form-hidden-field'
17541                 },
17542                 ibwrap
17543             ]
17544         };
17545         
17546         if(!this.multiple && this.showToggleBtn){
17547             
17548             var caret = {
17549                 cls: 'caret'
17550             };
17551             
17552             if (this.caret != false) {
17553                 caret = {
17554                      tag: 'i',
17555                      cls: 'fa fa-' + this.caret
17556                 };
17557                 
17558             }
17559             
17560             combobox.cn.push({
17561                 tag :'span',
17562                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17563                 cn : [
17564                     Roo.bootstrap.version == 3 ? caret : '',
17565                     {
17566                         tag: 'span',
17567                         cls: 'combobox-clear',
17568                         cn  : [
17569                             {
17570                                 tag : 'i',
17571                                 cls: 'icon-remove'
17572                             }
17573                         ]
17574                     }
17575                 ]
17576
17577             })
17578         }
17579         
17580         if(this.multiple){
17581             combobox.cls += ' roo-select2-container-multi';
17582         }
17583         
17584         var required =  this.allowBlank ?  {
17585                     tag : 'i',
17586                     style: 'display: none'
17587                 } : {
17588                    tag : 'i',
17589                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17590                    tooltip : 'This field is required'
17591                 };
17592         
17593         var align = this.labelAlign || this.parentLabelAlign();
17594         
17595         if (align ==='left' && this.fieldLabel.length) {
17596
17597             cfg.cn = [
17598                 required,
17599                 {
17600                     tag: 'label',
17601                     cls : 'control-label col-form-label',
17602                     html : this.fieldLabel
17603
17604                 },
17605                 {
17606                     cls : 'roo-combobox-wrap ', 
17607                     cn: [
17608                         combobox
17609                     ]
17610                 }
17611             ];
17612             
17613             var labelCfg = cfg.cn[1];
17614             var contentCfg = cfg.cn[2];
17615             
17616
17617             if(this.indicatorpos == 'right'){
17618                 cfg.cn = [
17619                     {
17620                         tag: 'label',
17621                         'for' :  id,
17622                         cls : 'control-label col-form-label',
17623                         cn : [
17624                             {
17625                                 tag : 'span',
17626                                 html : this.fieldLabel
17627                             },
17628                             required
17629                         ]
17630                     },
17631                     {
17632                         cls : "roo-combobox-wrap ",
17633                         cn: [
17634                             combobox
17635                         ]
17636                     }
17637
17638                 ];
17639                 
17640                 labelCfg = cfg.cn[0];
17641                 contentCfg = cfg.cn[1];
17642             }
17643             
17644            
17645             
17646             if(this.labelWidth > 12){
17647                 labelCfg.style = "width: " + this.labelWidth + 'px';
17648             }
17649            
17650             if(this.labelWidth < 13 && this.labelmd == 0){
17651                 this.labelmd = this.labelWidth;
17652             }
17653             
17654             if(this.labellg > 0){
17655                 labelCfg.cls += ' col-lg-' + this.labellg;
17656                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17657             }
17658             
17659             if(this.labelmd > 0){
17660                 labelCfg.cls += ' col-md-' + this.labelmd;
17661                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17662             }
17663             
17664             if(this.labelsm > 0){
17665                 labelCfg.cls += ' col-sm-' + this.labelsm;
17666                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17667             }
17668             
17669             if(this.labelxs > 0){
17670                 labelCfg.cls += ' col-xs-' + this.labelxs;
17671                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17672             }
17673                 
17674                 
17675         } else if ( this.fieldLabel.length) {
17676             cfg.cn = [
17677                required,
17678                 {
17679                     tag: 'label',
17680                     cls : 'control-label',
17681                     html : this.fieldLabel
17682
17683                 },
17684                 {
17685                     cls : '', 
17686                     cn: [
17687                         combobox
17688                     ]
17689                 }
17690             ];
17691             
17692             if(this.indicatorpos == 'right'){
17693                 cfg.cn = [
17694                     {
17695                         tag: 'label',
17696                         cls : 'control-label',
17697                         html : this.fieldLabel,
17698                         cn : [
17699                             required
17700                         ]
17701                     },
17702                     {
17703                         cls : '', 
17704                         cn: [
17705                             combobox
17706                         ]
17707                     }
17708                 ];
17709             }
17710         } else {
17711             cfg.cn = combobox;    
17712         }
17713         
17714         
17715         var settings = this;
17716         
17717         ['xs','sm','md','lg'].map(function(size){
17718             if (settings[size]) {
17719                 cfg.cls += ' col-' + size + '-' + settings[size];
17720             }
17721         });
17722         
17723         return cfg;
17724     },
17725     
17726     initTouchView : function()
17727     {
17728         this.renderTouchView();
17729         
17730         this.touchViewEl.on('scroll', function(){
17731             this.el.dom.scrollTop = 0;
17732         }, this);
17733         
17734         this.originalValue = this.getValue();
17735         
17736         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17737         
17738         this.inputEl().on("click", this.showTouchView, this);
17739         if (this.triggerEl) {
17740             this.triggerEl.on("click", this.showTouchView, this);
17741         }
17742         
17743         
17744         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17745         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17746         
17747         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17748         
17749         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17750         this.store.on('load', this.onTouchViewLoad, this);
17751         this.store.on('loadexception', this.onTouchViewLoadException, this);
17752         
17753         if(this.hiddenName){
17754             
17755             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17756             
17757             this.hiddenField.dom.value =
17758                 this.hiddenValue !== undefined ? this.hiddenValue :
17759                 this.value !== undefined ? this.value : '';
17760         
17761             this.el.dom.removeAttribute('name');
17762             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17763         }
17764         
17765         if(this.multiple){
17766             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17767             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17768         }
17769         
17770         if(this.removable && !this.multiple){
17771             var close = this.closeTriggerEl();
17772             if(close){
17773                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17774                 close.on('click', this.removeBtnClick, this, close);
17775             }
17776         }
17777         /*
17778          * fix the bug in Safari iOS8
17779          */
17780         this.inputEl().on("focus", function(e){
17781             document.activeElement.blur();
17782         }, this);
17783         
17784         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17785         
17786         return;
17787         
17788         
17789     },
17790     
17791     renderTouchView : function()
17792     {
17793         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17794         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17795         
17796         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17797         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17798         
17799         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17800         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17801         this.touchViewBodyEl.setStyle('overflow', 'auto');
17802         
17803         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17804         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17805         
17806         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17807         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17808         
17809     },
17810     
17811     showTouchView : function()
17812     {
17813         if(this.disabled){
17814             return;
17815         }
17816         
17817         this.touchViewHeaderEl.hide();
17818
17819         if(this.modalTitle.length){
17820             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17821             this.touchViewHeaderEl.show();
17822         }
17823
17824         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17825         this.touchViewEl.show();
17826
17827         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17828         
17829         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17830         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17831
17832         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17833
17834         if(this.modalTitle.length){
17835             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17836         }
17837         
17838         this.touchViewBodyEl.setHeight(bodyHeight);
17839
17840         if(this.animate){
17841             var _this = this;
17842             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17843         }else{
17844             this.touchViewEl.addClass(['in','show']);
17845         }
17846         
17847         if(this._touchViewMask){
17848             Roo.get(document.body).addClass("x-body-masked");
17849             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17850             this._touchViewMask.setStyle('z-index', 10000);
17851             this._touchViewMask.addClass('show');
17852         }
17853         
17854         this.doTouchViewQuery();
17855         
17856     },
17857     
17858     hideTouchView : function()
17859     {
17860         this.touchViewEl.removeClass(['in','show']);
17861
17862         if(this.animate){
17863             var _this = this;
17864             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17865         }else{
17866             this.touchViewEl.setStyle('display', 'none');
17867         }
17868         
17869         if(this._touchViewMask){
17870             this._touchViewMask.removeClass('show');
17871             Roo.get(document.body).removeClass("x-body-masked");
17872         }
17873     },
17874     
17875     setTouchViewValue : function()
17876     {
17877         if(this.multiple){
17878             this.clearItem();
17879         
17880             var _this = this;
17881
17882             Roo.each(this.tickItems, function(o){
17883                 this.addItem(o);
17884             }, this);
17885         }
17886         
17887         this.hideTouchView();
17888     },
17889     
17890     doTouchViewQuery : function()
17891     {
17892         var qe = {
17893             query: '',
17894             forceAll: true,
17895             combo: this,
17896             cancel:false
17897         };
17898         
17899         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17900             return false;
17901         }
17902         
17903         if(!this.alwaysQuery || this.mode == 'local'){
17904             this.onTouchViewLoad();
17905             return;
17906         }
17907         
17908         this.store.load();
17909     },
17910     
17911     onTouchViewBeforeLoad : function(combo,opts)
17912     {
17913         return;
17914     },
17915
17916     // private
17917     onTouchViewLoad : function()
17918     {
17919         if(this.store.getCount() < 1){
17920             this.onTouchViewEmptyResults();
17921             return;
17922         }
17923         
17924         this.clearTouchView();
17925         
17926         var rawValue = this.getRawValue();
17927         
17928         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17929         
17930         this.tickItems = [];
17931         
17932         this.store.data.each(function(d, rowIndex){
17933             var row = this.touchViewListGroup.createChild(template);
17934             
17935             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17936                 row.addClass(d.data.cls);
17937             }
17938             
17939             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17940                 var cfg = {
17941                     data : d.data,
17942                     html : d.data[this.displayField]
17943                 };
17944                 
17945                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17946                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17947                 }
17948             }
17949             row.removeClass('selected');
17950             if(!this.multiple && this.valueField &&
17951                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17952             {
17953                 // radio buttons..
17954                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955                 row.addClass('selected');
17956             }
17957             
17958             if(this.multiple && this.valueField &&
17959                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17960             {
17961                 
17962                 // checkboxes...
17963                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17964                 this.tickItems.push(d.data);
17965             }
17966             
17967             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17968             
17969         }, this);
17970         
17971         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17972         
17973         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17974
17975         if(this.modalTitle.length){
17976             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17977         }
17978
17979         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17980         
17981         if(this.mobile_restrict_height && listHeight < bodyHeight){
17982             this.touchViewBodyEl.setHeight(listHeight);
17983         }
17984         
17985         var _this = this;
17986         
17987         if(firstChecked && listHeight > bodyHeight){
17988             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17989         }
17990         
17991     },
17992     
17993     onTouchViewLoadException : function()
17994     {
17995         this.hideTouchView();
17996     },
17997     
17998     onTouchViewEmptyResults : function()
17999     {
18000         this.clearTouchView();
18001         
18002         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18003         
18004         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18005         
18006     },
18007     
18008     clearTouchView : function()
18009     {
18010         this.touchViewListGroup.dom.innerHTML = '';
18011     },
18012     
18013     onTouchViewClick : function(e, el, o)
18014     {
18015         e.preventDefault();
18016         
18017         var row = o.row;
18018         var rowIndex = o.rowIndex;
18019         
18020         var r = this.store.getAt(rowIndex);
18021         
18022         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18023             
18024             if(!this.multiple){
18025                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18026                     c.dom.removeAttribute('checked');
18027                 }, this);
18028
18029                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18030
18031                 this.setFromData(r.data);
18032
18033                 var close = this.closeTriggerEl();
18034
18035                 if(close){
18036                     close.show();
18037                 }
18038
18039                 this.hideTouchView();
18040
18041                 this.fireEvent('select', this, r, rowIndex);
18042
18043                 return;
18044             }
18045
18046             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18047                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18048                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18049                 return;
18050             }
18051
18052             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18053             this.addItem(r.data);
18054             this.tickItems.push(r.data);
18055         }
18056     },
18057     
18058     getAutoCreateNativeIOS : function()
18059     {
18060         var cfg = {
18061             cls: 'form-group' //input-group,
18062         };
18063         
18064         var combobox =  {
18065             tag: 'select',
18066             cls : 'roo-ios-select'
18067         };
18068         
18069         if (this.name) {
18070             combobox.name = this.name;
18071         }
18072         
18073         if (this.disabled) {
18074             combobox.disabled = true;
18075         }
18076         
18077         var settings = this;
18078         
18079         ['xs','sm','md','lg'].map(function(size){
18080             if (settings[size]) {
18081                 cfg.cls += ' col-' + size + '-' + settings[size];
18082             }
18083         });
18084         
18085         cfg.cn = combobox;
18086         
18087         return cfg;
18088         
18089     },
18090     
18091     initIOSView : function()
18092     {
18093         this.store.on('load', this.onIOSViewLoad, this);
18094         
18095         return;
18096     },
18097     
18098     onIOSViewLoad : function()
18099     {
18100         if(this.store.getCount() < 1){
18101             return;
18102         }
18103         
18104         this.clearIOSView();
18105         
18106         if(this.allowBlank) {
18107             
18108             var default_text = '-- SELECT --';
18109             
18110             if(this.placeholder.length){
18111                 default_text = this.placeholder;
18112             }
18113             
18114             if(this.emptyTitle.length){
18115                 default_text += ' - ' + this.emptyTitle + ' -';
18116             }
18117             
18118             var opt = this.inputEl().createChild({
18119                 tag: 'option',
18120                 value : 0,
18121                 html : default_text
18122             });
18123             
18124             var o = {};
18125             o[this.valueField] = 0;
18126             o[this.displayField] = default_text;
18127             
18128             this.ios_options.push({
18129                 data : o,
18130                 el : opt
18131             });
18132             
18133         }
18134         
18135         this.store.data.each(function(d, rowIndex){
18136             
18137             var html = '';
18138             
18139             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18140                 html = d.data[this.displayField];
18141             }
18142             
18143             var value = '';
18144             
18145             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18146                 value = d.data[this.valueField];
18147             }
18148             
18149             var option = {
18150                 tag: 'option',
18151                 value : value,
18152                 html : html
18153             };
18154             
18155             if(this.value == d.data[this.valueField]){
18156                 option['selected'] = true;
18157             }
18158             
18159             var opt = this.inputEl().createChild(option);
18160             
18161             this.ios_options.push({
18162                 data : d.data,
18163                 el : opt
18164             });
18165             
18166         }, this);
18167         
18168         this.inputEl().on('change', function(){
18169            this.fireEvent('select', this);
18170         }, this);
18171         
18172     },
18173     
18174     clearIOSView: function()
18175     {
18176         this.inputEl().dom.innerHTML = '';
18177         
18178         this.ios_options = [];
18179     },
18180     
18181     setIOSValue: function(v)
18182     {
18183         this.value = v;
18184         
18185         if(!this.ios_options){
18186             return;
18187         }
18188         
18189         Roo.each(this.ios_options, function(opts){
18190            
18191            opts.el.dom.removeAttribute('selected');
18192            
18193            if(opts.data[this.valueField] != v){
18194                return;
18195            }
18196            
18197            opts.el.dom.setAttribute('selected', true);
18198            
18199         }, this);
18200     }
18201
18202     /** 
18203     * @cfg {Boolean} grow 
18204     * @hide 
18205     */
18206     /** 
18207     * @cfg {Number} growMin 
18208     * @hide 
18209     */
18210     /** 
18211     * @cfg {Number} growMax 
18212     * @hide 
18213     */
18214     /**
18215      * @hide
18216      * @method autoSize
18217      */
18218 });
18219
18220 Roo.apply(Roo.bootstrap.ComboBox,  {
18221     
18222     header : {
18223         tag: 'div',
18224         cls: 'modal-header',
18225         cn: [
18226             {
18227                 tag: 'h4',
18228                 cls: 'modal-title'
18229             }
18230         ]
18231     },
18232     
18233     body : {
18234         tag: 'div',
18235         cls: 'modal-body',
18236         cn: [
18237             {
18238                 tag: 'ul',
18239                 cls: 'list-group'
18240             }
18241         ]
18242     },
18243     
18244     listItemRadio : {
18245         tag: 'li',
18246         cls: 'list-group-item',
18247         cn: [
18248             {
18249                 tag: 'span',
18250                 cls: 'roo-combobox-list-group-item-value'
18251             },
18252             {
18253                 tag: 'div',
18254                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18255                 cn: [
18256                     {
18257                         tag: 'input',
18258                         type: 'radio'
18259                     },
18260                     {
18261                         tag: 'label'
18262                     }
18263                 ]
18264             }
18265         ]
18266     },
18267     
18268     listItemCheckbox : {
18269         tag: 'li',
18270         cls: 'list-group-item',
18271         cn: [
18272             {
18273                 tag: 'span',
18274                 cls: 'roo-combobox-list-group-item-value'
18275             },
18276             {
18277                 tag: 'div',
18278                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18279                 cn: [
18280                     {
18281                         tag: 'input',
18282                         type: 'checkbox'
18283                     },
18284                     {
18285                         tag: 'label'
18286                     }
18287                 ]
18288             }
18289         ]
18290     },
18291     
18292     emptyResult : {
18293         tag: 'div',
18294         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18295     },
18296     
18297     footer : {
18298         tag: 'div',
18299         cls: 'modal-footer',
18300         cn: [
18301             {
18302                 tag: 'div',
18303                 cls: 'row',
18304                 cn: [
18305                     {
18306                         tag: 'div',
18307                         cls: 'col-xs-6 text-left',
18308                         cn: {
18309                             tag: 'button',
18310                             cls: 'btn btn-danger roo-touch-view-cancel',
18311                             html: 'Cancel'
18312                         }
18313                     },
18314                     {
18315                         tag: 'div',
18316                         cls: 'col-xs-6 text-right',
18317                         cn: {
18318                             tag: 'button',
18319                             cls: 'btn btn-success roo-touch-view-ok',
18320                             html: 'OK'
18321                         }
18322                     }
18323                 ]
18324             }
18325         ]
18326         
18327     }
18328 });
18329
18330 Roo.apply(Roo.bootstrap.ComboBox,  {
18331     
18332     touchViewTemplate : {
18333         tag: 'div',
18334         cls: 'modal fade roo-combobox-touch-view',
18335         cn: [
18336             {
18337                 tag: 'div',
18338                 cls: 'modal-dialog',
18339                 style : 'position:fixed', // we have to fix position....
18340                 cn: [
18341                     {
18342                         tag: 'div',
18343                         cls: 'modal-content',
18344                         cn: [
18345                             Roo.bootstrap.ComboBox.header,
18346                             Roo.bootstrap.ComboBox.body,
18347                             Roo.bootstrap.ComboBox.footer
18348                         ]
18349                     }
18350                 ]
18351             }
18352         ]
18353     }
18354 });/*
18355  * Based on:
18356  * Ext JS Library 1.1.1
18357  * Copyright(c) 2006-2007, Ext JS, LLC.
18358  *
18359  * Originally Released Under LGPL - original licence link has changed is not relivant.
18360  *
18361  * Fork - LGPL
18362  * <script type="text/javascript">
18363  */
18364
18365 /**
18366  * @class Roo.View
18367  * @extends Roo.util.Observable
18368  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18369  * This class also supports single and multi selection modes. <br>
18370  * Create a data model bound view:
18371  <pre><code>
18372  var store = new Roo.data.Store(...);
18373
18374  var view = new Roo.View({
18375     el : "my-element",
18376     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18377  
18378     singleSelect: true,
18379     selectedClass: "ydataview-selected",
18380     store: store
18381  });
18382
18383  // listen for node click?
18384  view.on("click", function(vw, index, node, e){
18385  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18386  });
18387
18388  // load XML data
18389  dataModel.load("foobar.xml");
18390  </code></pre>
18391  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18392  * <br><br>
18393  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18394  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18395  * 
18396  * Note: old style constructor is still suported (container, template, config)
18397  * 
18398  * @constructor
18399  * Create a new View
18400  * @param {Object} config The config object
18401  * 
18402  */
18403 Roo.View = function(config, depreciated_tpl, depreciated_config){
18404     
18405     this.parent = false;
18406     
18407     if (typeof(depreciated_tpl) == 'undefined') {
18408         // new way.. - universal constructor.
18409         Roo.apply(this, config);
18410         this.el  = Roo.get(this.el);
18411     } else {
18412         // old format..
18413         this.el  = Roo.get(config);
18414         this.tpl = depreciated_tpl;
18415         Roo.apply(this, depreciated_config);
18416     }
18417     this.wrapEl  = this.el.wrap().wrap();
18418     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18419     
18420     
18421     if(typeof(this.tpl) == "string"){
18422         this.tpl = new Roo.Template(this.tpl);
18423     } else {
18424         // support xtype ctors..
18425         this.tpl = new Roo.factory(this.tpl, Roo);
18426     }
18427     
18428     
18429     this.tpl.compile();
18430     
18431     /** @private */
18432     this.addEvents({
18433         /**
18434          * @event beforeclick
18435          * Fires before a click is processed. Returns false to cancel the default action.
18436          * @param {Roo.View} this
18437          * @param {Number} index The index of the target node
18438          * @param {HTMLElement} node The target node
18439          * @param {Roo.EventObject} e The raw event object
18440          */
18441             "beforeclick" : true,
18442         /**
18443          * @event click
18444          * Fires when a template node is clicked.
18445          * @param {Roo.View} this
18446          * @param {Number} index The index of the target node
18447          * @param {HTMLElement} node The target node
18448          * @param {Roo.EventObject} e The raw event object
18449          */
18450             "click" : true,
18451         /**
18452          * @event dblclick
18453          * Fires when a template node is double clicked.
18454          * @param {Roo.View} this
18455          * @param {Number} index The index of the target node
18456          * @param {HTMLElement} node The target node
18457          * @param {Roo.EventObject} e The raw event object
18458          */
18459             "dblclick" : true,
18460         /**
18461          * @event contextmenu
18462          * Fires when a template node is right clicked.
18463          * @param {Roo.View} this
18464          * @param {Number} index The index of the target node
18465          * @param {HTMLElement} node The target node
18466          * @param {Roo.EventObject} e The raw event object
18467          */
18468             "contextmenu" : true,
18469         /**
18470          * @event selectionchange
18471          * Fires when the selected nodes change.
18472          * @param {Roo.View} this
18473          * @param {Array} selections Array of the selected nodes
18474          */
18475             "selectionchange" : true,
18476     
18477         /**
18478          * @event beforeselect
18479          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18480          * @param {Roo.View} this
18481          * @param {HTMLElement} node The node to be selected
18482          * @param {Array} selections Array of currently selected nodes
18483          */
18484             "beforeselect" : true,
18485         /**
18486          * @event preparedata
18487          * Fires on every row to render, to allow you to change the data.
18488          * @param {Roo.View} this
18489          * @param {Object} data to be rendered (change this)
18490          */
18491           "preparedata" : true
18492           
18493           
18494         });
18495
18496
18497
18498     this.el.on({
18499         "click": this.onClick,
18500         "dblclick": this.onDblClick,
18501         "contextmenu": this.onContextMenu,
18502         scope:this
18503     });
18504
18505     this.selections = [];
18506     this.nodes = [];
18507     this.cmp = new Roo.CompositeElementLite([]);
18508     if(this.store){
18509         this.store = Roo.factory(this.store, Roo.data);
18510         this.setStore(this.store, true);
18511     }
18512     
18513     if ( this.footer && this.footer.xtype) {
18514            
18515          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18516         
18517         this.footer.dataSource = this.store;
18518         this.footer.container = fctr;
18519         this.footer = Roo.factory(this.footer, Roo);
18520         fctr.insertFirst(this.el);
18521         
18522         // this is a bit insane - as the paging toolbar seems to detach the el..
18523 //        dom.parentNode.parentNode.parentNode
18524          // they get detached?
18525     }
18526     
18527     
18528     Roo.View.superclass.constructor.call(this);
18529     
18530     
18531 };
18532
18533 Roo.extend(Roo.View, Roo.util.Observable, {
18534     
18535      /**
18536      * @cfg {Roo.data.Store} store Data store to load data from.
18537      */
18538     store : false,
18539     
18540     /**
18541      * @cfg {String|Roo.Element} el The container element.
18542      */
18543     el : '',
18544     
18545     /**
18546      * @cfg {String|Roo.Template} tpl The template used by this View 
18547      */
18548     tpl : false,
18549     /**
18550      * @cfg {String} dataName the named area of the template to use as the data area
18551      *                          Works with domtemplates roo-name="name"
18552      */
18553     dataName: false,
18554     /**
18555      * @cfg {String} selectedClass The css class to add to selected nodes
18556      */
18557     selectedClass : "x-view-selected",
18558      /**
18559      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18560      */
18561     emptyText : "",
18562     
18563     /**
18564      * @cfg {String} text to display on mask (default Loading)
18565      */
18566     mask : false,
18567     /**
18568      * @cfg {Boolean} multiSelect Allow multiple selection
18569      */
18570     multiSelect : false,
18571     /**
18572      * @cfg {Boolean} singleSelect Allow single selection
18573      */
18574     singleSelect:  false,
18575     
18576     /**
18577      * @cfg {Boolean} toggleSelect - selecting 
18578      */
18579     toggleSelect : false,
18580     
18581     /**
18582      * @cfg {Boolean} tickable - selecting 
18583      */
18584     tickable : false,
18585     
18586     /**
18587      * Returns the element this view is bound to.
18588      * @return {Roo.Element}
18589      */
18590     getEl : function(){
18591         return this.wrapEl;
18592     },
18593     
18594     
18595
18596     /**
18597      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18598      */
18599     refresh : function(){
18600         //Roo.log('refresh');
18601         var t = this.tpl;
18602         
18603         // if we are using something like 'domtemplate', then
18604         // the what gets used is:
18605         // t.applySubtemplate(NAME, data, wrapping data..)
18606         // the outer template then get' applied with
18607         //     the store 'extra data'
18608         // and the body get's added to the
18609         //      roo-name="data" node?
18610         //      <span class='roo-tpl-{name}'></span> ?????
18611         
18612         
18613         
18614         this.clearSelections();
18615         this.el.update("");
18616         var html = [];
18617         var records = this.store.getRange();
18618         if(records.length < 1) {
18619             
18620             // is this valid??  = should it render a template??
18621             
18622             this.el.update(this.emptyText);
18623             return;
18624         }
18625         var el = this.el;
18626         if (this.dataName) {
18627             this.el.update(t.apply(this.store.meta)); //????
18628             el = this.el.child('.roo-tpl-' + this.dataName);
18629         }
18630         
18631         for(var i = 0, len = records.length; i < len; i++){
18632             var data = this.prepareData(records[i].data, i, records[i]);
18633             this.fireEvent("preparedata", this, data, i, records[i]);
18634             
18635             var d = Roo.apply({}, data);
18636             
18637             if(this.tickable){
18638                 Roo.apply(d, {'roo-id' : Roo.id()});
18639                 
18640                 var _this = this;
18641             
18642                 Roo.each(this.parent.item, function(item){
18643                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18644                         return;
18645                     }
18646                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18647                 });
18648             }
18649             
18650             html[html.length] = Roo.util.Format.trim(
18651                 this.dataName ?
18652                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18653                     t.apply(d)
18654             );
18655         }
18656         
18657         
18658         
18659         el.update(html.join(""));
18660         this.nodes = el.dom.childNodes;
18661         this.updateIndexes(0);
18662     },
18663     
18664
18665     /**
18666      * Function to override to reformat the data that is sent to
18667      * the template for each node.
18668      * DEPRICATED - use the preparedata event handler.
18669      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18670      * a JSON object for an UpdateManager bound view).
18671      */
18672     prepareData : function(data, index, record)
18673     {
18674         this.fireEvent("preparedata", this, data, index, record);
18675         return data;
18676     },
18677
18678     onUpdate : function(ds, record){
18679         // Roo.log('on update');   
18680         this.clearSelections();
18681         var index = this.store.indexOf(record);
18682         var n = this.nodes[index];
18683         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18684         n.parentNode.removeChild(n);
18685         this.updateIndexes(index, index);
18686     },
18687
18688     
18689     
18690 // --------- FIXME     
18691     onAdd : function(ds, records, index)
18692     {
18693         //Roo.log(['on Add', ds, records, index] );        
18694         this.clearSelections();
18695         if(this.nodes.length == 0){
18696             this.refresh();
18697             return;
18698         }
18699         var n = this.nodes[index];
18700         for(var i = 0, len = records.length; i < len; i++){
18701             var d = this.prepareData(records[i].data, i, records[i]);
18702             if(n){
18703                 this.tpl.insertBefore(n, d);
18704             }else{
18705                 
18706                 this.tpl.append(this.el, d);
18707             }
18708         }
18709         this.updateIndexes(index);
18710     },
18711
18712     onRemove : function(ds, record, index){
18713        // Roo.log('onRemove');
18714         this.clearSelections();
18715         var el = this.dataName  ?
18716             this.el.child('.roo-tpl-' + this.dataName) :
18717             this.el; 
18718         
18719         el.dom.removeChild(this.nodes[index]);
18720         this.updateIndexes(index);
18721     },
18722
18723     /**
18724      * Refresh an individual node.
18725      * @param {Number} index
18726      */
18727     refreshNode : function(index){
18728         this.onUpdate(this.store, this.store.getAt(index));
18729     },
18730
18731     updateIndexes : function(startIndex, endIndex){
18732         var ns = this.nodes;
18733         startIndex = startIndex || 0;
18734         endIndex = endIndex || ns.length - 1;
18735         for(var i = startIndex; i <= endIndex; i++){
18736             ns[i].nodeIndex = i;
18737         }
18738     },
18739
18740     /**
18741      * Changes the data store this view uses and refresh the view.
18742      * @param {Store} store
18743      */
18744     setStore : function(store, initial){
18745         if(!initial && this.store){
18746             this.store.un("datachanged", this.refresh);
18747             this.store.un("add", this.onAdd);
18748             this.store.un("remove", this.onRemove);
18749             this.store.un("update", this.onUpdate);
18750             this.store.un("clear", this.refresh);
18751             this.store.un("beforeload", this.onBeforeLoad);
18752             this.store.un("load", this.onLoad);
18753             this.store.un("loadexception", this.onLoad);
18754         }
18755         if(store){
18756           
18757             store.on("datachanged", this.refresh, this);
18758             store.on("add", this.onAdd, this);
18759             store.on("remove", this.onRemove, this);
18760             store.on("update", this.onUpdate, this);
18761             store.on("clear", this.refresh, this);
18762             store.on("beforeload", this.onBeforeLoad, this);
18763             store.on("load", this.onLoad, this);
18764             store.on("loadexception", this.onLoad, this);
18765         }
18766         
18767         if(store){
18768             this.refresh();
18769         }
18770     },
18771     /**
18772      * onbeforeLoad - masks the loading area.
18773      *
18774      */
18775     onBeforeLoad : function(store,opts)
18776     {
18777          //Roo.log('onBeforeLoad');   
18778         if (!opts.add) {
18779             this.el.update("");
18780         }
18781         this.el.mask(this.mask ? this.mask : "Loading" ); 
18782     },
18783     onLoad : function ()
18784     {
18785         this.el.unmask();
18786     },
18787     
18788
18789     /**
18790      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18791      * @param {HTMLElement} node
18792      * @return {HTMLElement} The template node
18793      */
18794     findItemFromChild : function(node){
18795         var el = this.dataName  ?
18796             this.el.child('.roo-tpl-' + this.dataName,true) :
18797             this.el.dom; 
18798         
18799         if(!node || node.parentNode == el){
18800                     return node;
18801             }
18802             var p = node.parentNode;
18803             while(p && p != el){
18804             if(p.parentNode == el){
18805                 return p;
18806             }
18807             p = p.parentNode;
18808         }
18809             return null;
18810     },
18811
18812     /** @ignore */
18813     onClick : function(e){
18814         var item = this.findItemFromChild(e.getTarget());
18815         if(item){
18816             var index = this.indexOf(item);
18817             if(this.onItemClick(item, index, e) !== false){
18818                 this.fireEvent("click", this, index, item, e);
18819             }
18820         }else{
18821             this.clearSelections();
18822         }
18823     },
18824
18825     /** @ignore */
18826     onContextMenu : function(e){
18827         var item = this.findItemFromChild(e.getTarget());
18828         if(item){
18829             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18830         }
18831     },
18832
18833     /** @ignore */
18834     onDblClick : function(e){
18835         var item = this.findItemFromChild(e.getTarget());
18836         if(item){
18837             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18838         }
18839     },
18840
18841     onItemClick : function(item, index, e)
18842     {
18843         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18844             return false;
18845         }
18846         if (this.toggleSelect) {
18847             var m = this.isSelected(item) ? 'unselect' : 'select';
18848             //Roo.log(m);
18849             var _t = this;
18850             _t[m](item, true, false);
18851             return true;
18852         }
18853         if(this.multiSelect || this.singleSelect){
18854             if(this.multiSelect && e.shiftKey && this.lastSelection){
18855                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18856             }else{
18857                 this.select(item, this.multiSelect && e.ctrlKey);
18858                 this.lastSelection = item;
18859             }
18860             
18861             if(!this.tickable){
18862                 e.preventDefault();
18863             }
18864             
18865         }
18866         return true;
18867     },
18868
18869     /**
18870      * Get the number of selected nodes.
18871      * @return {Number}
18872      */
18873     getSelectionCount : function(){
18874         return this.selections.length;
18875     },
18876
18877     /**
18878      * Get the currently selected nodes.
18879      * @return {Array} An array of HTMLElements
18880      */
18881     getSelectedNodes : function(){
18882         return this.selections;
18883     },
18884
18885     /**
18886      * Get the indexes of the selected nodes.
18887      * @return {Array}
18888      */
18889     getSelectedIndexes : function(){
18890         var indexes = [], s = this.selections;
18891         for(var i = 0, len = s.length; i < len; i++){
18892             indexes.push(s[i].nodeIndex);
18893         }
18894         return indexes;
18895     },
18896
18897     /**
18898      * Clear all selections
18899      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18900      */
18901     clearSelections : function(suppressEvent){
18902         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18903             this.cmp.elements = this.selections;
18904             this.cmp.removeClass(this.selectedClass);
18905             this.selections = [];
18906             if(!suppressEvent){
18907                 this.fireEvent("selectionchange", this, this.selections);
18908             }
18909         }
18910     },
18911
18912     /**
18913      * Returns true if the passed node is selected
18914      * @param {HTMLElement/Number} node The node or node index
18915      * @return {Boolean}
18916      */
18917     isSelected : function(node){
18918         var s = this.selections;
18919         if(s.length < 1){
18920             return false;
18921         }
18922         node = this.getNode(node);
18923         return s.indexOf(node) !== -1;
18924     },
18925
18926     /**
18927      * Selects nodes.
18928      * @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
18929      * @param {Boolean} keepExisting (optional) true to keep existing selections
18930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18931      */
18932     select : function(nodeInfo, keepExisting, suppressEvent){
18933         if(nodeInfo instanceof Array){
18934             if(!keepExisting){
18935                 this.clearSelections(true);
18936             }
18937             for(var i = 0, len = nodeInfo.length; i < len; i++){
18938                 this.select(nodeInfo[i], true, true);
18939             }
18940             return;
18941         } 
18942         var node = this.getNode(nodeInfo);
18943         if(!node || this.isSelected(node)){
18944             return; // already selected.
18945         }
18946         if(!keepExisting){
18947             this.clearSelections(true);
18948         }
18949         
18950         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18951             Roo.fly(node).addClass(this.selectedClass);
18952             this.selections.push(node);
18953             if(!suppressEvent){
18954                 this.fireEvent("selectionchange", this, this.selections);
18955             }
18956         }
18957         
18958         
18959     },
18960       /**
18961      * Unselects nodes.
18962      * @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
18963      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18964      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18965      */
18966     unselect : function(nodeInfo, keepExisting, suppressEvent)
18967     {
18968         if(nodeInfo instanceof Array){
18969             Roo.each(this.selections, function(s) {
18970                 this.unselect(s, nodeInfo);
18971             }, this);
18972             return;
18973         }
18974         var node = this.getNode(nodeInfo);
18975         if(!node || !this.isSelected(node)){
18976             //Roo.log("not selected");
18977             return; // not selected.
18978         }
18979         // fireevent???
18980         var ns = [];
18981         Roo.each(this.selections, function(s) {
18982             if (s == node ) {
18983                 Roo.fly(node).removeClass(this.selectedClass);
18984
18985                 return;
18986             }
18987             ns.push(s);
18988         },this);
18989         
18990         this.selections= ns;
18991         this.fireEvent("selectionchange", this, this.selections);
18992     },
18993
18994     /**
18995      * Gets a template node.
18996      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18997      * @return {HTMLElement} The node or null if it wasn't found
18998      */
18999     getNode : function(nodeInfo){
19000         if(typeof nodeInfo == "string"){
19001             return document.getElementById(nodeInfo);
19002         }else if(typeof nodeInfo == "number"){
19003             return this.nodes[nodeInfo];
19004         }
19005         return nodeInfo;
19006     },
19007
19008     /**
19009      * Gets a range template nodes.
19010      * @param {Number} startIndex
19011      * @param {Number} endIndex
19012      * @return {Array} An array of nodes
19013      */
19014     getNodes : function(start, end){
19015         var ns = this.nodes;
19016         start = start || 0;
19017         end = typeof end == "undefined" ? ns.length - 1 : end;
19018         var nodes = [];
19019         if(start <= end){
19020             for(var i = start; i <= end; i++){
19021                 nodes.push(ns[i]);
19022             }
19023         } else{
19024             for(var i = start; i >= end; i--){
19025                 nodes.push(ns[i]);
19026             }
19027         }
19028         return nodes;
19029     },
19030
19031     /**
19032      * Finds the index of the passed node
19033      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19034      * @return {Number} The index of the node or -1
19035      */
19036     indexOf : function(node){
19037         node = this.getNode(node);
19038         if(typeof node.nodeIndex == "number"){
19039             return node.nodeIndex;
19040         }
19041         var ns = this.nodes;
19042         for(var i = 0, len = ns.length; i < len; i++){
19043             if(ns[i] == node){
19044                 return i;
19045             }
19046         }
19047         return -1;
19048     }
19049 });
19050 /*
19051  * - LGPL
19052  *
19053  * based on jquery fullcalendar
19054  * 
19055  */
19056
19057 Roo.bootstrap = Roo.bootstrap || {};
19058 /**
19059  * @class Roo.bootstrap.Calendar
19060  * @extends Roo.bootstrap.Component
19061  * Bootstrap Calendar class
19062  * @cfg {Boolean} loadMask (true|false) default false
19063  * @cfg {Object} header generate the user specific header of the calendar, default false
19064
19065  * @constructor
19066  * Create a new Container
19067  * @param {Object} config The config object
19068  */
19069
19070
19071
19072 Roo.bootstrap.Calendar = function(config){
19073     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19074      this.addEvents({
19075         /**
19076              * @event select
19077              * Fires when a date is selected
19078              * @param {DatePicker} this
19079              * @param {Date} date The selected date
19080              */
19081         'select': true,
19082         /**
19083              * @event monthchange
19084              * Fires when the displayed month changes 
19085              * @param {DatePicker} this
19086              * @param {Date} date The selected month
19087              */
19088         'monthchange': true,
19089         /**
19090              * @event evententer
19091              * Fires when mouse over an event
19092              * @param {Calendar} this
19093              * @param {event} Event
19094              */
19095         'evententer': true,
19096         /**
19097              * @event eventleave
19098              * Fires when the mouse leaves an
19099              * @param {Calendar} this
19100              * @param {event}
19101              */
19102         'eventleave': true,
19103         /**
19104              * @event eventclick
19105              * Fires when the mouse click an
19106              * @param {Calendar} this
19107              * @param {event}
19108              */
19109         'eventclick': true
19110         
19111     });
19112
19113 };
19114
19115 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19116     
19117      /**
19118      * @cfg {Number} startDay
19119      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19120      */
19121     startDay : 0,
19122     
19123     loadMask : false,
19124     
19125     header : false,
19126       
19127     getAutoCreate : function(){
19128         
19129         
19130         var fc_button = function(name, corner, style, content ) {
19131             return Roo.apply({},{
19132                 tag : 'span',
19133                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19134                          (corner.length ?
19135                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19136                             ''
19137                         ),
19138                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19139                 unselectable: 'on'
19140             });
19141         };
19142         
19143         var header = {};
19144         
19145         if(!this.header){
19146             header = {
19147                 tag : 'table',
19148                 cls : 'fc-header',
19149                 style : 'width:100%',
19150                 cn : [
19151                     {
19152                         tag: 'tr',
19153                         cn : [
19154                             {
19155                                 tag : 'td',
19156                                 cls : 'fc-header-left',
19157                                 cn : [
19158                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19159                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19160                                     { tag: 'span', cls: 'fc-header-space' },
19161                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19162
19163
19164                                 ]
19165                             },
19166
19167                             {
19168                                 tag : 'td',
19169                                 cls : 'fc-header-center',
19170                                 cn : [
19171                                     {
19172                                         tag: 'span',
19173                                         cls: 'fc-header-title',
19174                                         cn : {
19175                                             tag: 'H2',
19176                                             html : 'month / year'
19177                                         }
19178                                     }
19179
19180                                 ]
19181                             },
19182                             {
19183                                 tag : 'td',
19184                                 cls : 'fc-header-right',
19185                                 cn : [
19186                               /*      fc_button('month', 'left', '', 'month' ),
19187                                     fc_button('week', '', '', 'week' ),
19188                                     fc_button('day', 'right', '', 'day' )
19189                                 */    
19190
19191                                 ]
19192                             }
19193
19194                         ]
19195                     }
19196                 ]
19197             };
19198         }
19199         
19200         header = this.header;
19201         
19202        
19203         var cal_heads = function() {
19204             var ret = [];
19205             // fixme - handle this.
19206             
19207             for (var i =0; i < Date.dayNames.length; i++) {
19208                 var d = Date.dayNames[i];
19209                 ret.push({
19210                     tag: 'th',
19211                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19212                     html : d.substring(0,3)
19213                 });
19214                 
19215             }
19216             ret[0].cls += ' fc-first';
19217             ret[6].cls += ' fc-last';
19218             return ret;
19219         };
19220         var cal_cell = function(n) {
19221             return  {
19222                 tag: 'td',
19223                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19224                 cn : [
19225                     {
19226                         cn : [
19227                             {
19228                                 cls: 'fc-day-number',
19229                                 html: 'D'
19230                             },
19231                             {
19232                                 cls: 'fc-day-content',
19233                              
19234                                 cn : [
19235                                      {
19236                                         style: 'position: relative;' // height: 17px;
19237                                     }
19238                                 ]
19239                             }
19240                             
19241                             
19242                         ]
19243                     }
19244                 ]
19245                 
19246             }
19247         };
19248         var cal_rows = function() {
19249             
19250             var ret = [];
19251             for (var r = 0; r < 6; r++) {
19252                 var row= {
19253                     tag : 'tr',
19254                     cls : 'fc-week',
19255                     cn : []
19256                 };
19257                 
19258                 for (var i =0; i < Date.dayNames.length; i++) {
19259                     var d = Date.dayNames[i];
19260                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19261
19262                 }
19263                 row.cn[0].cls+=' fc-first';
19264                 row.cn[0].cn[0].style = 'min-height:90px';
19265                 row.cn[6].cls+=' fc-last';
19266                 ret.push(row);
19267                 
19268             }
19269             ret[0].cls += ' fc-first';
19270             ret[4].cls += ' fc-prev-last';
19271             ret[5].cls += ' fc-last';
19272             return ret;
19273             
19274         };
19275         
19276         var cal_table = {
19277             tag: 'table',
19278             cls: 'fc-border-separate',
19279             style : 'width:100%',
19280             cellspacing  : 0,
19281             cn : [
19282                 { 
19283                     tag: 'thead',
19284                     cn : [
19285                         { 
19286                             tag: 'tr',
19287                             cls : 'fc-first fc-last',
19288                             cn : cal_heads()
19289                         }
19290                     ]
19291                 },
19292                 { 
19293                     tag: 'tbody',
19294                     cn : cal_rows()
19295                 }
19296                   
19297             ]
19298         };
19299          
19300          var cfg = {
19301             cls : 'fc fc-ltr',
19302             cn : [
19303                 header,
19304                 {
19305                     cls : 'fc-content',
19306                     style : "position: relative;",
19307                     cn : [
19308                         {
19309                             cls : 'fc-view fc-view-month fc-grid',
19310                             style : 'position: relative',
19311                             unselectable : 'on',
19312                             cn : [
19313                                 {
19314                                     cls : 'fc-event-container',
19315                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19316                                 },
19317                                 cal_table
19318                             ]
19319                         }
19320                     ]
19321     
19322                 }
19323            ] 
19324             
19325         };
19326         
19327          
19328         
19329         return cfg;
19330     },
19331     
19332     
19333     initEvents : function()
19334     {
19335         if(!this.store){
19336             throw "can not find store for calendar";
19337         }
19338         
19339         var mark = {
19340             tag: "div",
19341             cls:"x-dlg-mask",
19342             style: "text-align:center",
19343             cn: [
19344                 {
19345                     tag: "div",
19346                     style: "background-color:white;width:50%;margin:250 auto",
19347                     cn: [
19348                         {
19349                             tag: "img",
19350                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19351                         },
19352                         {
19353                             tag: "span",
19354                             html: "Loading"
19355                         }
19356                         
19357                     ]
19358                 }
19359             ]
19360         };
19361         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19362         
19363         var size = this.el.select('.fc-content', true).first().getSize();
19364         this.maskEl.setSize(size.width, size.height);
19365         this.maskEl.enableDisplayMode("block");
19366         if(!this.loadMask){
19367             this.maskEl.hide();
19368         }
19369         
19370         this.store = Roo.factory(this.store, Roo.data);
19371         this.store.on('load', this.onLoad, this);
19372         this.store.on('beforeload', this.onBeforeLoad, this);
19373         
19374         this.resize();
19375         
19376         this.cells = this.el.select('.fc-day',true);
19377         //Roo.log(this.cells);
19378         this.textNodes = this.el.query('.fc-day-number');
19379         this.cells.addClassOnOver('fc-state-hover');
19380         
19381         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19382         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19383         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19384         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19385         
19386         this.on('monthchange', this.onMonthChange, this);
19387         
19388         this.update(new Date().clearTime());
19389     },
19390     
19391     resize : function() {
19392         var sz  = this.el.getSize();
19393         
19394         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19395         this.el.select('.fc-day-content div',true).setHeight(34);
19396     },
19397     
19398     
19399     // private
19400     showPrevMonth : function(e){
19401         this.update(this.activeDate.add("mo", -1));
19402     },
19403     showToday : function(e){
19404         this.update(new Date().clearTime());
19405     },
19406     // private
19407     showNextMonth : function(e){
19408         this.update(this.activeDate.add("mo", 1));
19409     },
19410
19411     // private
19412     showPrevYear : function(){
19413         this.update(this.activeDate.add("y", -1));
19414     },
19415
19416     // private
19417     showNextYear : function(){
19418         this.update(this.activeDate.add("y", 1));
19419     },
19420
19421     
19422    // private
19423     update : function(date)
19424     {
19425         var vd = this.activeDate;
19426         this.activeDate = date;
19427 //        if(vd && this.el){
19428 //            var t = date.getTime();
19429 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19430 //                Roo.log('using add remove');
19431 //                
19432 //                this.fireEvent('monthchange', this, date);
19433 //                
19434 //                this.cells.removeClass("fc-state-highlight");
19435 //                this.cells.each(function(c){
19436 //                   if(c.dateValue == t){
19437 //                       c.addClass("fc-state-highlight");
19438 //                       setTimeout(function(){
19439 //                            try{c.dom.firstChild.focus();}catch(e){}
19440 //                       }, 50);
19441 //                       return false;
19442 //                   }
19443 //                   return true;
19444 //                });
19445 //                return;
19446 //            }
19447 //        }
19448         
19449         var days = date.getDaysInMonth();
19450         
19451         var firstOfMonth = date.getFirstDateOfMonth();
19452         var startingPos = firstOfMonth.getDay()-this.startDay;
19453         
19454         if(startingPos < this.startDay){
19455             startingPos += 7;
19456         }
19457         
19458         var pm = date.add(Date.MONTH, -1);
19459         var prevStart = pm.getDaysInMonth()-startingPos;
19460 //        
19461         this.cells = this.el.select('.fc-day',true);
19462         this.textNodes = this.el.query('.fc-day-number');
19463         this.cells.addClassOnOver('fc-state-hover');
19464         
19465         var cells = this.cells.elements;
19466         var textEls = this.textNodes;
19467         
19468         Roo.each(cells, function(cell){
19469             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19470         });
19471         
19472         days += startingPos;
19473
19474         // convert everything to numbers so it's fast
19475         var day = 86400000;
19476         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19477         //Roo.log(d);
19478         //Roo.log(pm);
19479         //Roo.log(prevStart);
19480         
19481         var today = new Date().clearTime().getTime();
19482         var sel = date.clearTime().getTime();
19483         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19484         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19485         var ddMatch = this.disabledDatesRE;
19486         var ddText = this.disabledDatesText;
19487         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19488         var ddaysText = this.disabledDaysText;
19489         var format = this.format;
19490         
19491         var setCellClass = function(cal, cell){
19492             cell.row = 0;
19493             cell.events = [];
19494             cell.more = [];
19495             //Roo.log('set Cell Class');
19496             cell.title = "";
19497             var t = d.getTime();
19498             
19499             //Roo.log(d);
19500             
19501             cell.dateValue = t;
19502             if(t == today){
19503                 cell.className += " fc-today";
19504                 cell.className += " fc-state-highlight";
19505                 cell.title = cal.todayText;
19506             }
19507             if(t == sel){
19508                 // disable highlight in other month..
19509                 //cell.className += " fc-state-highlight";
19510                 
19511             }
19512             // disabling
19513             if(t < min) {
19514                 cell.className = " fc-state-disabled";
19515                 cell.title = cal.minText;
19516                 return;
19517             }
19518             if(t > max) {
19519                 cell.className = " fc-state-disabled";
19520                 cell.title = cal.maxText;
19521                 return;
19522             }
19523             if(ddays){
19524                 if(ddays.indexOf(d.getDay()) != -1){
19525                     cell.title = ddaysText;
19526                     cell.className = " fc-state-disabled";
19527                 }
19528             }
19529             if(ddMatch && format){
19530                 var fvalue = d.dateFormat(format);
19531                 if(ddMatch.test(fvalue)){
19532                     cell.title = ddText.replace("%0", fvalue);
19533                     cell.className = " fc-state-disabled";
19534                 }
19535             }
19536             
19537             if (!cell.initialClassName) {
19538                 cell.initialClassName = cell.dom.className;
19539             }
19540             
19541             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19542         };
19543
19544         var i = 0;
19545         
19546         for(; i < startingPos; i++) {
19547             textEls[i].innerHTML = (++prevStart);
19548             d.setDate(d.getDate()+1);
19549             
19550             cells[i].className = "fc-past fc-other-month";
19551             setCellClass(this, cells[i]);
19552         }
19553         
19554         var intDay = 0;
19555         
19556         for(; i < days; i++){
19557             intDay = i - startingPos + 1;
19558             textEls[i].innerHTML = (intDay);
19559             d.setDate(d.getDate()+1);
19560             
19561             cells[i].className = ''; // "x-date-active";
19562             setCellClass(this, cells[i]);
19563         }
19564         var extraDays = 0;
19565         
19566         for(; i < 42; i++) {
19567             textEls[i].innerHTML = (++extraDays);
19568             d.setDate(d.getDate()+1);
19569             
19570             cells[i].className = "fc-future fc-other-month";
19571             setCellClass(this, cells[i]);
19572         }
19573         
19574         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19575         
19576         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19577         
19578         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19579         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19580         
19581         if(totalRows != 6){
19582             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19583             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19584         }
19585         
19586         this.fireEvent('monthchange', this, date);
19587         
19588         
19589         /*
19590         if(!this.internalRender){
19591             var main = this.el.dom.firstChild;
19592             var w = main.offsetWidth;
19593             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19594             Roo.fly(main).setWidth(w);
19595             this.internalRender = true;
19596             // opera does not respect the auto grow header center column
19597             // then, after it gets a width opera refuses to recalculate
19598             // without a second pass
19599             if(Roo.isOpera && !this.secondPass){
19600                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19601                 this.secondPass = true;
19602                 this.update.defer(10, this, [date]);
19603             }
19604         }
19605         */
19606         
19607     },
19608     
19609     findCell : function(dt) {
19610         dt = dt.clearTime().getTime();
19611         var ret = false;
19612         this.cells.each(function(c){
19613             //Roo.log("check " +c.dateValue + '?=' + dt);
19614             if(c.dateValue == dt){
19615                 ret = c;
19616                 return false;
19617             }
19618             return true;
19619         });
19620         
19621         return ret;
19622     },
19623     
19624     findCells : function(ev) {
19625         var s = ev.start.clone().clearTime().getTime();
19626        // Roo.log(s);
19627         var e= ev.end.clone().clearTime().getTime();
19628        // Roo.log(e);
19629         var ret = [];
19630         this.cells.each(function(c){
19631              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19632             
19633             if(c.dateValue > e){
19634                 return ;
19635             }
19636             if(c.dateValue < s){
19637                 return ;
19638             }
19639             ret.push(c);
19640         });
19641         
19642         return ret;    
19643     },
19644     
19645 //    findBestRow: function(cells)
19646 //    {
19647 //        var ret = 0;
19648 //        
19649 //        for (var i =0 ; i < cells.length;i++) {
19650 //            ret  = Math.max(cells[i].rows || 0,ret);
19651 //        }
19652 //        return ret;
19653 //        
19654 //    },
19655     
19656     
19657     addItem : function(ev)
19658     {
19659         // look for vertical location slot in
19660         var cells = this.findCells(ev);
19661         
19662 //        ev.row = this.findBestRow(cells);
19663         
19664         // work out the location.
19665         
19666         var crow = false;
19667         var rows = [];
19668         for(var i =0; i < cells.length; i++) {
19669             
19670             cells[i].row = cells[0].row;
19671             
19672             if(i == 0){
19673                 cells[i].row = cells[i].row + 1;
19674             }
19675             
19676             if (!crow) {
19677                 crow = {
19678                     start : cells[i],
19679                     end :  cells[i]
19680                 };
19681                 continue;
19682             }
19683             if (crow.start.getY() == cells[i].getY()) {
19684                 // on same row.
19685                 crow.end = cells[i];
19686                 continue;
19687             }
19688             // different row.
19689             rows.push(crow);
19690             crow = {
19691                 start: cells[i],
19692                 end : cells[i]
19693             };
19694             
19695         }
19696         
19697         rows.push(crow);
19698         ev.els = [];
19699         ev.rows = rows;
19700         ev.cells = cells;
19701         
19702         cells[0].events.push(ev);
19703         
19704         this.calevents.push(ev);
19705     },
19706     
19707     clearEvents: function() {
19708         
19709         if(!this.calevents){
19710             return;
19711         }
19712         
19713         Roo.each(this.cells.elements, function(c){
19714             c.row = 0;
19715             c.events = [];
19716             c.more = [];
19717         });
19718         
19719         Roo.each(this.calevents, function(e) {
19720             Roo.each(e.els, function(el) {
19721                 el.un('mouseenter' ,this.onEventEnter, this);
19722                 el.un('mouseleave' ,this.onEventLeave, this);
19723                 el.remove();
19724             },this);
19725         },this);
19726         
19727         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19728             e.remove();
19729         });
19730         
19731     },
19732     
19733     renderEvents: function()
19734     {   
19735         var _this = this;
19736         
19737         this.cells.each(function(c) {
19738             
19739             if(c.row < 5){
19740                 return;
19741             }
19742             
19743             var ev = c.events;
19744             
19745             var r = 4;
19746             if(c.row != c.events.length){
19747                 r = 4 - (4 - (c.row - c.events.length));
19748             }
19749             
19750             c.events = ev.slice(0, r);
19751             c.more = ev.slice(r);
19752             
19753             if(c.more.length && c.more.length == 1){
19754                 c.events.push(c.more.pop());
19755             }
19756             
19757             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19758             
19759         });
19760             
19761         this.cells.each(function(c) {
19762             
19763             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19764             
19765             
19766             for (var e = 0; e < c.events.length; e++){
19767                 var ev = c.events[e];
19768                 var rows = ev.rows;
19769                 
19770                 for(var i = 0; i < rows.length; i++) {
19771                 
19772                     // how many rows should it span..
19773
19774                     var  cfg = {
19775                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19776                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19777
19778                         unselectable : "on",
19779                         cn : [
19780                             {
19781                                 cls: 'fc-event-inner',
19782                                 cn : [
19783     //                                {
19784     //                                  tag:'span',
19785     //                                  cls: 'fc-event-time',
19786     //                                  html : cells.length > 1 ? '' : ev.time
19787     //                                },
19788                                     {
19789                                       tag:'span',
19790                                       cls: 'fc-event-title',
19791                                       html : String.format('{0}', ev.title)
19792                                     }
19793
19794
19795                                 ]
19796                             },
19797                             {
19798                                 cls: 'ui-resizable-handle ui-resizable-e',
19799                                 html : '&nbsp;&nbsp;&nbsp'
19800                             }
19801
19802                         ]
19803                     };
19804
19805                     if (i == 0) {
19806                         cfg.cls += ' fc-event-start';
19807                     }
19808                     if ((i+1) == rows.length) {
19809                         cfg.cls += ' fc-event-end';
19810                     }
19811
19812                     var ctr = _this.el.select('.fc-event-container',true).first();
19813                     var cg = ctr.createChild(cfg);
19814
19815                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19816                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19817
19818                     var r = (c.more.length) ? 1 : 0;
19819                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19820                     cg.setWidth(ebox.right - sbox.x -2);
19821
19822                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19823                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19824                     cg.on('click', _this.onEventClick, _this, ev);
19825
19826                     ev.els.push(cg);
19827                     
19828                 }
19829                 
19830             }
19831             
19832             
19833             if(c.more.length){
19834                 var  cfg = {
19835                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19836                     style : 'position: absolute',
19837                     unselectable : "on",
19838                     cn : [
19839                         {
19840                             cls: 'fc-event-inner',
19841                             cn : [
19842                                 {
19843                                   tag:'span',
19844                                   cls: 'fc-event-title',
19845                                   html : 'More'
19846                                 }
19847
19848
19849                             ]
19850                         },
19851                         {
19852                             cls: 'ui-resizable-handle ui-resizable-e',
19853                             html : '&nbsp;&nbsp;&nbsp'
19854                         }
19855
19856                     ]
19857                 };
19858
19859                 var ctr = _this.el.select('.fc-event-container',true).first();
19860                 var cg = ctr.createChild(cfg);
19861
19862                 var sbox = c.select('.fc-day-content',true).first().getBox();
19863                 var ebox = c.select('.fc-day-content',true).first().getBox();
19864                 //Roo.log(cg);
19865                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19866                 cg.setWidth(ebox.right - sbox.x -2);
19867
19868                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19869                 
19870             }
19871             
19872         });
19873         
19874         
19875         
19876     },
19877     
19878     onEventEnter: function (e, el,event,d) {
19879         this.fireEvent('evententer', this, el, event);
19880     },
19881     
19882     onEventLeave: function (e, el,event,d) {
19883         this.fireEvent('eventleave', this, el, event);
19884     },
19885     
19886     onEventClick: function (e, el,event,d) {
19887         this.fireEvent('eventclick', this, el, event);
19888     },
19889     
19890     onMonthChange: function () {
19891         this.store.load();
19892     },
19893     
19894     onMoreEventClick: function(e, el, more)
19895     {
19896         var _this = this;
19897         
19898         this.calpopover.placement = 'right';
19899         this.calpopover.setTitle('More');
19900         
19901         this.calpopover.setContent('');
19902         
19903         var ctr = this.calpopover.el.select('.popover-content', true).first();
19904         
19905         Roo.each(more, function(m){
19906             var cfg = {
19907                 cls : 'fc-event-hori fc-event-draggable',
19908                 html : m.title
19909             };
19910             var cg = ctr.createChild(cfg);
19911             
19912             cg.on('click', _this.onEventClick, _this, m);
19913         });
19914         
19915         this.calpopover.show(el);
19916         
19917         
19918     },
19919     
19920     onLoad: function () 
19921     {   
19922         this.calevents = [];
19923         var cal = this;
19924         
19925         if(this.store.getCount() > 0){
19926             this.store.data.each(function(d){
19927                cal.addItem({
19928                     id : d.data.id,
19929                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19930                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19931                     time : d.data.start_time,
19932                     title : d.data.title,
19933                     description : d.data.description,
19934                     venue : d.data.venue
19935                 });
19936             });
19937         }
19938         
19939         this.renderEvents();
19940         
19941         if(this.calevents.length && this.loadMask){
19942             this.maskEl.hide();
19943         }
19944     },
19945     
19946     onBeforeLoad: function()
19947     {
19948         this.clearEvents();
19949         if(this.loadMask){
19950             this.maskEl.show();
19951         }
19952     }
19953 });
19954
19955  
19956  /*
19957  * - LGPL
19958  *
19959  * element
19960  * 
19961  */
19962
19963 /**
19964  * @class Roo.bootstrap.Popover
19965  * @extends Roo.bootstrap.Component
19966  * Bootstrap Popover class
19967  * @cfg {String} html contents of the popover   (or false to use children..)
19968  * @cfg {String} title of popover (or false to hide)
19969  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19970  * @cfg {String} trigger click || hover (or false to trigger manually)
19971  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19972  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19973  *      - if false and it has a 'parent' then it will be automatically added to that element
19974  *      - if string - Roo.get  will be called 
19975  * @cfg {Number} delay - delay before showing
19976  
19977  * @constructor
19978  * Create a new Popover
19979  * @param {Object} config The config object
19980  */
19981
19982 Roo.bootstrap.Popover = function(config){
19983     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19984     
19985     this.addEvents({
19986         // raw events
19987          /**
19988          * @event show
19989          * After the popover show
19990          * 
19991          * @param {Roo.bootstrap.Popover} this
19992          */
19993         "show" : true,
19994         /**
19995          * @event hide
19996          * After the popover hide
19997          * 
19998          * @param {Roo.bootstrap.Popover} this
19999          */
20000         "hide" : true
20001     });
20002 };
20003
20004 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20005     
20006     title: false,
20007     html: false,
20008     
20009     placement : 'right',
20010     trigger : 'hover', // hover
20011     modal : false,
20012     delay : 0,
20013     
20014     over: false,
20015     
20016     can_build_overlaid : false,
20017     
20018     maskEl : false, // the mask element
20019     headerEl : false,
20020     contentEl : false,
20021     alignEl : false, // when show is called with an element - this get's stored.
20022     
20023     getChildContainer : function()
20024     {
20025         return this.contentEl;
20026         
20027     },
20028     getPopoverHeader : function()
20029     {
20030         this.title = true; // flag not to hide it..
20031         this.headerEl.addClass('p-0');
20032         return this.headerEl
20033     },
20034     
20035     
20036     getAutoCreate : function(){
20037          
20038         var cfg = {
20039            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20040            style: 'display:block',
20041            cn : [
20042                 {
20043                     cls : 'arrow'
20044                 },
20045                 {
20046                     cls : 'popover-inner ',
20047                     cn : [
20048                         {
20049                             tag: 'h3',
20050                             cls: 'popover-title popover-header',
20051                             html : this.title === false ? '' : this.title
20052                         },
20053                         {
20054                             cls : 'popover-content popover-body '  + (this.cls || ''),
20055                             html : this.html || ''
20056                         }
20057                     ]
20058                     
20059                 }
20060            ]
20061         };
20062         
20063         return cfg;
20064     },
20065     /**
20066      * @param {string} the title
20067      */
20068     setTitle: function(str)
20069     {
20070         this.title = str;
20071         if (this.el) {
20072             this.headerEl.dom.innerHTML = str;
20073         }
20074         
20075     },
20076     /**
20077      * @param {string} the body content
20078      */
20079     setContent: function(str)
20080     {
20081         this.html = str;
20082         if (this.contentEl) {
20083             this.contentEl.dom.innerHTML = str;
20084         }
20085         
20086     },
20087     // as it get's added to the bottom of the page.
20088     onRender : function(ct, position)
20089     {
20090         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20091         
20092         
20093         
20094         if(!this.el){
20095             var cfg = Roo.apply({},  this.getAutoCreate());
20096             cfg.id = Roo.id();
20097             
20098             if (this.cls) {
20099                 cfg.cls += ' ' + this.cls;
20100             }
20101             if (this.style) {
20102                 cfg.style = this.style;
20103             }
20104             //Roo.log("adding to ");
20105             this.el = Roo.get(document.body).createChild(cfg, position);
20106 //            Roo.log(this.el);
20107         }
20108         
20109         this.contentEl = this.el.select('.popover-content',true).first();
20110         this.headerEl =  this.el.select('.popover-title',true).first();
20111         
20112         var nitems = [];
20113         if(typeof(this.items) != 'undefined'){
20114             var items = this.items;
20115             delete this.items;
20116
20117             for(var i =0;i < items.length;i++) {
20118                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20119             }
20120         }
20121
20122         this.items = nitems;
20123         
20124         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20125         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20126         
20127         
20128         
20129         this.initEvents();
20130     },
20131     
20132     resizeMask : function()
20133     {
20134         this.maskEl.setSize(
20135             Roo.lib.Dom.getViewWidth(true),
20136             Roo.lib.Dom.getViewHeight(true)
20137         );
20138     },
20139     
20140     initEvents : function()
20141     {
20142         
20143         if (!this.modal) { 
20144             Roo.bootstrap.Popover.register(this);
20145         }
20146          
20147         this.arrowEl = this.el.select('.arrow',true).first();
20148         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20149         this.el.enableDisplayMode('block');
20150         this.el.hide();
20151  
20152         
20153         if (this.over === false && !this.parent()) {
20154             return; 
20155         }
20156         if (this.triggers === false) {
20157             return;
20158         }
20159          
20160         // support parent
20161         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20162         var triggers = this.trigger ? this.trigger.split(' ') : [];
20163         Roo.each(triggers, function(trigger) {
20164         
20165             if (trigger == 'click') {
20166                 on_el.on('click', this.toggle, this);
20167             } else if (trigger != 'manual') {
20168                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20169                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20170       
20171                 on_el.on(eventIn  ,this.enter, this);
20172                 on_el.on(eventOut, this.leave, this);
20173             }
20174         }, this);
20175     },
20176     
20177     
20178     // private
20179     timeout : null,
20180     hoverState : null,
20181     
20182     toggle : function () {
20183         this.hoverState == 'in' ? this.leave() : this.enter();
20184     },
20185     
20186     enter : function () {
20187         
20188         clearTimeout(this.timeout);
20189     
20190         this.hoverState = 'in';
20191     
20192         if (!this.delay || !this.delay.show) {
20193             this.show();
20194             return;
20195         }
20196         var _t = this;
20197         this.timeout = setTimeout(function () {
20198             if (_t.hoverState == 'in') {
20199                 _t.show();
20200             }
20201         }, this.delay.show)
20202     },
20203     
20204     leave : function() {
20205         clearTimeout(this.timeout);
20206     
20207         this.hoverState = 'out';
20208     
20209         if (!this.delay || !this.delay.hide) {
20210             this.hide();
20211             return;
20212         }
20213         var _t = this;
20214         this.timeout = setTimeout(function () {
20215             if (_t.hoverState == 'out') {
20216                 _t.hide();
20217             }
20218         }, this.delay.hide)
20219     },
20220     /**
20221      * Show the popover
20222      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20223      * @param {string} (left|right|top|bottom) position
20224      */
20225     show : function (on_el, placement)
20226     {
20227         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20228         on_el = on_el || false; // default to false
20229          
20230         if (!on_el) {
20231             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20232                 on_el = this.parent().el;
20233             } else if (this.over) {
20234                 on_el = Roo.get(this.over);
20235             }
20236             
20237         }
20238         
20239         this.alignEl = Roo.get( on_el );
20240
20241         if (!this.el) {
20242             this.render(document.body);
20243         }
20244         
20245         
20246          
20247         
20248         if (this.title === false) {
20249             this.headerEl.hide();
20250         }
20251         
20252        
20253         this.el.show();
20254         this.el.dom.style.display = 'block';
20255          
20256  
20257         if (this.alignEl) {
20258             this.updatePosition(this.placement, true);
20259              
20260         } else {
20261             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20262             var es = this.el.getSize();
20263             var x = Roo.lib.Dom.getViewWidth()/2;
20264             var y = Roo.lib.Dom.getViewHeight()/2;
20265             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20266             
20267         }
20268
20269         
20270         //var arrow = this.el.select('.arrow',true).first();
20271         //arrow.set(align[2], 
20272         
20273         this.el.addClass('in');
20274         
20275          
20276         
20277         this.hoverState = 'in';
20278         
20279         if (this.modal) {
20280             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20281             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20282             this.maskEl.dom.style.display = 'block';
20283             this.maskEl.addClass('show');
20284         }
20285         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20286  
20287         this.fireEvent('show', this);
20288         
20289     },
20290     /**
20291      * fire this manually after loading a grid in the table for example
20292      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20293      * @param {Boolean} try and move it if we cant get right position.
20294      */
20295     updatePosition : function(placement, try_move)
20296     {
20297         // allow for calling with no parameters
20298         placement = placement   ? placement :  this.placement;
20299         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20300         
20301         this.el.removeClass([
20302             'fade','top','bottom', 'left', 'right','in',
20303             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20304         ]);
20305         this.el.addClass(placement + ' bs-popover-' + placement);
20306         
20307         if (!this.alignEl ) {
20308             return false;
20309         }
20310         
20311         switch (placement) {
20312             case 'right':
20313                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20314                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20315                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20316                     //normal display... or moved up/down.
20317                     this.el.setXY(offset);
20318                     var xy = this.alignEl.getAnchorXY('tr', false);
20319                     xy[0]+=2;xy[1]+=5;
20320                     this.arrowEl.setXY(xy);
20321                     return true;
20322                 }
20323                 // continue through...
20324                 return this.updatePosition('left', false);
20325                 
20326             
20327             case 'left':
20328                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20329                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20330                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20331                     //normal display... or moved up/down.
20332                     this.el.setXY(offset);
20333                     var xy = this.alignEl.getAnchorXY('tl', false);
20334                     xy[0]-=10;xy[1]+=5; // << fix me
20335                     this.arrowEl.setXY(xy);
20336                     return true;
20337                 }
20338                 // call self...
20339                 return this.updatePosition('right', false);
20340             
20341             case 'top':
20342                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20343                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20344                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20345                     //normal display... or moved up/down.
20346                     this.el.setXY(offset);
20347                     var xy = this.alignEl.getAnchorXY('t', false);
20348                     xy[1]-=10; // << fix me
20349                     this.arrowEl.setXY(xy);
20350                     return true;
20351                 }
20352                 // fall through
20353                return this.updatePosition('bottom', false);
20354             
20355             case 'bottom':
20356                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20357                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20358                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20359                     //normal display... or moved up/down.
20360                     this.el.setXY(offset);
20361                     var xy = this.alignEl.getAnchorXY('b', false);
20362                      xy[1]+=2; // << fix me
20363                     this.arrowEl.setXY(xy);
20364                     return true;
20365                 }
20366                 // fall through
20367                 return this.updatePosition('top', false);
20368                 
20369             
20370         }
20371         
20372         
20373         return false;
20374     },
20375     
20376     hide : function()
20377     {
20378         this.el.setXY([0,0]);
20379         this.el.removeClass('in');
20380         this.el.hide();
20381         this.hoverState = null;
20382         this.maskEl.hide(); // always..
20383         this.fireEvent('hide', this);
20384     }
20385     
20386 });
20387
20388
20389 Roo.apply(Roo.bootstrap.Popover, {
20390
20391     alignment : {
20392         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20393         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20394         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20395         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20396     },
20397     
20398     zIndex : 20001,
20399
20400     clickHander : false,
20401     
20402
20403     onMouseDown : function(e)
20404     {
20405         if (!e.getTarget(".roo-popover")) {
20406             this.hideAll();
20407         }
20408          
20409     },
20410     
20411     popups : [],
20412     
20413     register : function(popup)
20414     {
20415         if (!Roo.bootstrap.Popover.clickHandler) {
20416             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20417         }
20418         // hide other popups.
20419         this.hideAll();
20420         this.popups.push(popup);
20421     },
20422     hideAll : function()
20423     {
20424         this.popups.forEach(function(p) {
20425             p.hide();
20426         });
20427     }
20428
20429 });/*
20430  * - LGPL
20431  *
20432  * Card header - holder for the card header elements.
20433  * 
20434  */
20435
20436 /**
20437  * @class Roo.bootstrap.PopoverNav
20438  * @extends Roo.bootstrap.NavGroup
20439  * Bootstrap Popover header navigation class
20440  * @constructor
20441  * Create a new Popover Header Navigation 
20442  * @param {Object} config The config object
20443  */
20444
20445 Roo.bootstrap.PopoverNav = function(config){
20446     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20447 };
20448
20449 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20450     
20451     
20452     container_method : 'getPopoverHeader' 
20453     
20454      
20455     
20456     
20457    
20458 });
20459
20460  
20461
20462  /*
20463  * - LGPL
20464  *
20465  * Progress
20466  * 
20467  */
20468
20469 /**
20470  * @class Roo.bootstrap.Progress
20471  * @extends Roo.bootstrap.Component
20472  * Bootstrap Progress class
20473  * @cfg {Boolean} striped striped of the progress bar
20474  * @cfg {Boolean} active animated of the progress bar
20475  * 
20476  * 
20477  * @constructor
20478  * Create a new Progress
20479  * @param {Object} config The config object
20480  */
20481
20482 Roo.bootstrap.Progress = function(config){
20483     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20484 };
20485
20486 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20487     
20488     striped : false,
20489     active: false,
20490     
20491     getAutoCreate : function(){
20492         var cfg = {
20493             tag: 'div',
20494             cls: 'progress'
20495         };
20496         
20497         
20498         if(this.striped){
20499             cfg.cls += ' progress-striped';
20500         }
20501       
20502         if(this.active){
20503             cfg.cls += ' active';
20504         }
20505         
20506         
20507         return cfg;
20508     }
20509    
20510 });
20511
20512  
20513
20514  /*
20515  * - LGPL
20516  *
20517  * ProgressBar
20518  * 
20519  */
20520
20521 /**
20522  * @class Roo.bootstrap.ProgressBar
20523  * @extends Roo.bootstrap.Component
20524  * Bootstrap ProgressBar class
20525  * @cfg {Number} aria_valuenow aria-value now
20526  * @cfg {Number} aria_valuemin aria-value min
20527  * @cfg {Number} aria_valuemax aria-value max
20528  * @cfg {String} label label for the progress bar
20529  * @cfg {String} panel (success | info | warning | danger )
20530  * @cfg {String} role role of the progress bar
20531  * @cfg {String} sr_only text
20532  * 
20533  * 
20534  * @constructor
20535  * Create a new ProgressBar
20536  * @param {Object} config The config object
20537  */
20538
20539 Roo.bootstrap.ProgressBar = function(config){
20540     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20541 };
20542
20543 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20544     
20545     aria_valuenow : 0,
20546     aria_valuemin : 0,
20547     aria_valuemax : 100,
20548     label : false,
20549     panel : false,
20550     role : false,
20551     sr_only: false,
20552     
20553     getAutoCreate : function()
20554     {
20555         
20556         var cfg = {
20557             tag: 'div',
20558             cls: 'progress-bar',
20559             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20560         };
20561         
20562         if(this.sr_only){
20563             cfg.cn = {
20564                 tag: 'span',
20565                 cls: 'sr-only',
20566                 html: this.sr_only
20567             }
20568         }
20569         
20570         if(this.role){
20571             cfg.role = this.role;
20572         }
20573         
20574         if(this.aria_valuenow){
20575             cfg['aria-valuenow'] = this.aria_valuenow;
20576         }
20577         
20578         if(this.aria_valuemin){
20579             cfg['aria-valuemin'] = this.aria_valuemin;
20580         }
20581         
20582         if(this.aria_valuemax){
20583             cfg['aria-valuemax'] = this.aria_valuemax;
20584         }
20585         
20586         if(this.label && !this.sr_only){
20587             cfg.html = this.label;
20588         }
20589         
20590         if(this.panel){
20591             cfg.cls += ' progress-bar-' + this.panel;
20592         }
20593         
20594         return cfg;
20595     },
20596     
20597     update : function(aria_valuenow)
20598     {
20599         this.aria_valuenow = aria_valuenow;
20600         
20601         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20602     }
20603    
20604 });
20605
20606  
20607
20608  /*
20609  * - LGPL
20610  *
20611  * column
20612  * 
20613  */
20614
20615 /**
20616  * @class Roo.bootstrap.TabGroup
20617  * @extends Roo.bootstrap.Column
20618  * Bootstrap Column class
20619  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20620  * @cfg {Boolean} carousel true to make the group behave like a carousel
20621  * @cfg {Boolean} bullets show bullets for the panels
20622  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20623  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20624  * @cfg {Boolean} showarrow (true|false) show arrow default true
20625  * 
20626  * @constructor
20627  * Create a new TabGroup
20628  * @param {Object} config The config object
20629  */
20630
20631 Roo.bootstrap.TabGroup = function(config){
20632     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20633     if (!this.navId) {
20634         this.navId = Roo.id();
20635     }
20636     this.tabs = [];
20637     Roo.bootstrap.TabGroup.register(this);
20638     
20639 };
20640
20641 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20642     
20643     carousel : false,
20644     transition : false,
20645     bullets : 0,
20646     timer : 0,
20647     autoslide : false,
20648     slideFn : false,
20649     slideOnTouch : false,
20650     showarrow : true,
20651     
20652     getAutoCreate : function()
20653     {
20654         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20655         
20656         cfg.cls += ' tab-content';
20657         
20658         if (this.carousel) {
20659             cfg.cls += ' carousel slide';
20660             
20661             cfg.cn = [{
20662                cls : 'carousel-inner',
20663                cn : []
20664             }];
20665         
20666             if(this.bullets  && !Roo.isTouch){
20667                 
20668                 var bullets = {
20669                     cls : 'carousel-bullets',
20670                     cn : []
20671                 };
20672                
20673                 if(this.bullets_cls){
20674                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20675                 }
20676                 
20677                 bullets.cn.push({
20678                     cls : 'clear'
20679                 });
20680                 
20681                 cfg.cn[0].cn.push(bullets);
20682             }
20683             
20684             if(this.showarrow){
20685                 cfg.cn[0].cn.push({
20686                     tag : 'div',
20687                     class : 'carousel-arrow',
20688                     cn : [
20689                         {
20690                             tag : 'div',
20691                             class : 'carousel-prev',
20692                             cn : [
20693                                 {
20694                                     tag : 'i',
20695                                     class : 'fa fa-chevron-left'
20696                                 }
20697                             ]
20698                         },
20699                         {
20700                             tag : 'div',
20701                             class : 'carousel-next',
20702                             cn : [
20703                                 {
20704                                     tag : 'i',
20705                                     class : 'fa fa-chevron-right'
20706                                 }
20707                             ]
20708                         }
20709                     ]
20710                 });
20711             }
20712             
20713         }
20714         
20715         return cfg;
20716     },
20717     
20718     initEvents:  function()
20719     {
20720 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20721 //            this.el.on("touchstart", this.onTouchStart, this);
20722 //        }
20723         
20724         if(this.autoslide){
20725             var _this = this;
20726             
20727             this.slideFn = window.setInterval(function() {
20728                 _this.showPanelNext();
20729             }, this.timer);
20730         }
20731         
20732         if(this.showarrow){
20733             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20734             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20735         }
20736         
20737         
20738     },
20739     
20740 //    onTouchStart : function(e, el, o)
20741 //    {
20742 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20743 //            return;
20744 //        }
20745 //        
20746 //        this.showPanelNext();
20747 //    },
20748     
20749     
20750     getChildContainer : function()
20751     {
20752         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20753     },
20754     
20755     /**
20756     * register a Navigation item
20757     * @param {Roo.bootstrap.NavItem} the navitem to add
20758     */
20759     register : function(item)
20760     {
20761         this.tabs.push( item);
20762         item.navId = this.navId; // not really needed..
20763         this.addBullet();
20764     
20765     },
20766     
20767     getActivePanel : function()
20768     {
20769         var r = false;
20770         Roo.each(this.tabs, function(t) {
20771             if (t.active) {
20772                 r = t;
20773                 return false;
20774             }
20775             return null;
20776         });
20777         return r;
20778         
20779     },
20780     getPanelByName : function(n)
20781     {
20782         var r = false;
20783         Roo.each(this.tabs, function(t) {
20784             if (t.tabId == n) {
20785                 r = t;
20786                 return false;
20787             }
20788             return null;
20789         });
20790         return r;
20791     },
20792     indexOfPanel : function(p)
20793     {
20794         var r = false;
20795         Roo.each(this.tabs, function(t,i) {
20796             if (t.tabId == p.tabId) {
20797                 r = i;
20798                 return false;
20799             }
20800             return null;
20801         });
20802         return r;
20803     },
20804     /**
20805      * show a specific panel
20806      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20807      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20808      */
20809     showPanel : function (pan)
20810     {
20811         if(this.transition || typeof(pan) == 'undefined'){
20812             Roo.log("waiting for the transitionend");
20813             return false;
20814         }
20815         
20816         if (typeof(pan) == 'number') {
20817             pan = this.tabs[pan];
20818         }
20819         
20820         if (typeof(pan) == 'string') {
20821             pan = this.getPanelByName(pan);
20822         }
20823         
20824         var cur = this.getActivePanel();
20825         
20826         if(!pan || !cur){
20827             Roo.log('pan or acitve pan is undefined');
20828             return false;
20829         }
20830         
20831         if (pan.tabId == this.getActivePanel().tabId) {
20832             return true;
20833         }
20834         
20835         if (false === cur.fireEvent('beforedeactivate')) {
20836             return false;
20837         }
20838         
20839         if(this.bullets > 0 && !Roo.isTouch){
20840             this.setActiveBullet(this.indexOfPanel(pan));
20841         }
20842         
20843         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20844             
20845             //class="carousel-item carousel-item-next carousel-item-left"
20846             
20847             this.transition = true;
20848             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20849             var lr = dir == 'next' ? 'left' : 'right';
20850             pan.el.addClass(dir); // or prev
20851             pan.el.addClass('carousel-item-' + dir); // or prev
20852             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20853             cur.el.addClass(lr); // or right
20854             pan.el.addClass(lr);
20855             cur.el.addClass('carousel-item-' +lr); // or right
20856             pan.el.addClass('carousel-item-' +lr);
20857             
20858             
20859             var _this = this;
20860             cur.el.on('transitionend', function() {
20861                 Roo.log("trans end?");
20862                 
20863                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20864                 pan.setActive(true);
20865                 
20866                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20867                 cur.setActive(false);
20868                 
20869                 _this.transition = false;
20870                 
20871             }, this, { single:  true } );
20872             
20873             return true;
20874         }
20875         
20876         cur.setActive(false);
20877         pan.setActive(true);
20878         
20879         return true;
20880         
20881     },
20882     showPanelNext : function()
20883     {
20884         var i = this.indexOfPanel(this.getActivePanel());
20885         
20886         if (i >= this.tabs.length - 1 && !this.autoslide) {
20887             return;
20888         }
20889         
20890         if (i >= this.tabs.length - 1 && this.autoslide) {
20891             i = -1;
20892         }
20893         
20894         this.showPanel(this.tabs[i+1]);
20895     },
20896     
20897     showPanelPrev : function()
20898     {
20899         var i = this.indexOfPanel(this.getActivePanel());
20900         
20901         if (i  < 1 && !this.autoslide) {
20902             return;
20903         }
20904         
20905         if (i < 1 && this.autoslide) {
20906             i = this.tabs.length;
20907         }
20908         
20909         this.showPanel(this.tabs[i-1]);
20910     },
20911     
20912     
20913     addBullet: function()
20914     {
20915         if(!this.bullets || Roo.isTouch){
20916             return;
20917         }
20918         var ctr = this.el.select('.carousel-bullets',true).first();
20919         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20920         var bullet = ctr.createChild({
20921             cls : 'bullet bullet-' + i
20922         },ctr.dom.lastChild);
20923         
20924         
20925         var _this = this;
20926         
20927         bullet.on('click', (function(e, el, o, ii, t){
20928
20929             e.preventDefault();
20930
20931             this.showPanel(ii);
20932
20933             if(this.autoslide && this.slideFn){
20934                 clearInterval(this.slideFn);
20935                 this.slideFn = window.setInterval(function() {
20936                     _this.showPanelNext();
20937                 }, this.timer);
20938             }
20939
20940         }).createDelegate(this, [i, bullet], true));
20941                 
20942         
20943     },
20944      
20945     setActiveBullet : function(i)
20946     {
20947         if(Roo.isTouch){
20948             return;
20949         }
20950         
20951         Roo.each(this.el.select('.bullet', true).elements, function(el){
20952             el.removeClass('selected');
20953         });
20954
20955         var bullet = this.el.select('.bullet-' + i, true).first();
20956         
20957         if(!bullet){
20958             return;
20959         }
20960         
20961         bullet.addClass('selected');
20962     }
20963     
20964     
20965   
20966 });
20967
20968  
20969
20970  
20971  
20972 Roo.apply(Roo.bootstrap.TabGroup, {
20973     
20974     groups: {},
20975      /**
20976     * register a Navigation Group
20977     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20978     */
20979     register : function(navgrp)
20980     {
20981         this.groups[navgrp.navId] = navgrp;
20982         
20983     },
20984     /**
20985     * fetch a Navigation Group based on the navigation ID
20986     * if one does not exist , it will get created.
20987     * @param {string} the navgroup to add
20988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20989     */
20990     get: function(navId) {
20991         if (typeof(this.groups[navId]) == 'undefined') {
20992             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20993         }
20994         return this.groups[navId] ;
20995     }
20996     
20997     
20998     
20999 });
21000
21001  /*
21002  * - LGPL
21003  *
21004  * TabPanel
21005  * 
21006  */
21007
21008 /**
21009  * @class Roo.bootstrap.TabPanel
21010  * @extends Roo.bootstrap.Component
21011  * Bootstrap TabPanel class
21012  * @cfg {Boolean} active panel active
21013  * @cfg {String} html panel content
21014  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21015  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21016  * @cfg {String} href click to link..
21017  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21018  * 
21019  * 
21020  * @constructor
21021  * Create a new TabPanel
21022  * @param {Object} config The config object
21023  */
21024
21025 Roo.bootstrap.TabPanel = function(config){
21026     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21027     this.addEvents({
21028         /**
21029              * @event changed
21030              * Fires when the active status changes
21031              * @param {Roo.bootstrap.TabPanel} this
21032              * @param {Boolean} state the new state
21033             
21034          */
21035         'changed': true,
21036         /**
21037              * @event beforedeactivate
21038              * Fires before a tab is de-activated - can be used to do validation on a form.
21039              * @param {Roo.bootstrap.TabPanel} this
21040              * @return {Boolean} false if there is an error
21041             
21042          */
21043         'beforedeactivate': true
21044      });
21045     
21046     this.tabId = this.tabId || Roo.id();
21047   
21048 };
21049
21050 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21051     
21052     active: false,
21053     html: false,
21054     tabId: false,
21055     navId : false,
21056     href : '',
21057     touchSlide : false,
21058     getAutoCreate : function(){
21059         
21060         
21061         var cfg = {
21062             tag: 'div',
21063             // item is needed for carousel - not sure if it has any effect otherwise
21064             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21065             html: this.html || ''
21066         };
21067         
21068         if(this.active){
21069             cfg.cls += ' active';
21070         }
21071         
21072         if(this.tabId){
21073             cfg.tabId = this.tabId;
21074         }
21075         
21076         
21077         
21078         return cfg;
21079     },
21080     
21081     initEvents:  function()
21082     {
21083         var p = this.parent();
21084         
21085         this.navId = this.navId || p.navId;
21086         
21087         if (typeof(this.navId) != 'undefined') {
21088             // not really needed.. but just in case.. parent should be a NavGroup.
21089             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21090             
21091             tg.register(this);
21092             
21093             var i = tg.tabs.length - 1;
21094             
21095             if(this.active && tg.bullets > 0 && i < tg.bullets){
21096                 tg.setActiveBullet(i);
21097             }
21098         }
21099         
21100         this.el.on('click', this.onClick, this);
21101         
21102         if(Roo.isTouch && this.touchSlide){
21103             this.el.on("touchstart", this.onTouchStart, this);
21104             this.el.on("touchmove", this.onTouchMove, this);
21105             this.el.on("touchend", this.onTouchEnd, this);
21106         }
21107         
21108     },
21109     
21110     onRender : function(ct, position)
21111     {
21112         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21113     },
21114     
21115     setActive : function(state)
21116     {
21117         Roo.log("panel - set active " + this.tabId + "=" + state);
21118         
21119         this.active = state;
21120         if (!state) {
21121             this.el.removeClass('active');
21122             
21123         } else  if (!this.el.hasClass('active')) {
21124             this.el.addClass('active');
21125         }
21126         
21127         this.fireEvent('changed', this, state);
21128     },
21129     
21130     onClick : function(e)
21131     {
21132         e.preventDefault();
21133         
21134         if(!this.href.length){
21135             return;
21136         }
21137         
21138         window.location.href = this.href;
21139     },
21140     
21141     startX : 0,
21142     startY : 0,
21143     endX : 0,
21144     endY : 0,
21145     swiping : false,
21146     
21147     onTouchStart : function(e)
21148     {
21149         this.swiping = false;
21150         
21151         this.startX = e.browserEvent.touches[0].clientX;
21152         this.startY = e.browserEvent.touches[0].clientY;
21153     },
21154     
21155     onTouchMove : function(e)
21156     {
21157         this.swiping = true;
21158         
21159         this.endX = e.browserEvent.touches[0].clientX;
21160         this.endY = e.browserEvent.touches[0].clientY;
21161     },
21162     
21163     onTouchEnd : function(e)
21164     {
21165         if(!this.swiping){
21166             this.onClick(e);
21167             return;
21168         }
21169         
21170         var tabGroup = this.parent();
21171         
21172         if(this.endX > this.startX){ // swiping right
21173             tabGroup.showPanelPrev();
21174             return;
21175         }
21176         
21177         if(this.startX > this.endX){ // swiping left
21178             tabGroup.showPanelNext();
21179             return;
21180         }
21181     }
21182     
21183     
21184 });
21185  
21186
21187  
21188
21189  /*
21190  * - LGPL
21191  *
21192  * DateField
21193  * 
21194  */
21195
21196 /**
21197  * @class Roo.bootstrap.DateField
21198  * @extends Roo.bootstrap.Input
21199  * Bootstrap DateField class
21200  * @cfg {Number} weekStart default 0
21201  * @cfg {String} viewMode default empty, (months|years)
21202  * @cfg {String} minViewMode default empty, (months|years)
21203  * @cfg {Number} startDate default -Infinity
21204  * @cfg {Number} endDate default Infinity
21205  * @cfg {Boolean} todayHighlight default false
21206  * @cfg {Boolean} todayBtn default false
21207  * @cfg {Boolean} calendarWeeks default false
21208  * @cfg {Object} daysOfWeekDisabled default empty
21209  * @cfg {Boolean} singleMode default false (true | false)
21210  * 
21211  * @cfg {Boolean} keyboardNavigation default true
21212  * @cfg {String} language default en
21213  * 
21214  * @constructor
21215  * Create a new DateField
21216  * @param {Object} config The config object
21217  */
21218
21219 Roo.bootstrap.DateField = function(config){
21220     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21221      this.addEvents({
21222             /**
21223              * @event show
21224              * Fires when this field show.
21225              * @param {Roo.bootstrap.DateField} this
21226              * @param {Mixed} date The date value
21227              */
21228             show : true,
21229             /**
21230              * @event show
21231              * Fires when this field hide.
21232              * @param {Roo.bootstrap.DateField} this
21233              * @param {Mixed} date The date value
21234              */
21235             hide : true,
21236             /**
21237              * @event select
21238              * Fires when select a date.
21239              * @param {Roo.bootstrap.DateField} this
21240              * @param {Mixed} date The date value
21241              */
21242             select : true,
21243             /**
21244              * @event beforeselect
21245              * Fires when before select a date.
21246              * @param {Roo.bootstrap.DateField} this
21247              * @param {Mixed} date The date value
21248              */
21249             beforeselect : true
21250         });
21251 };
21252
21253 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21254     
21255     /**
21256      * @cfg {String} format
21257      * The default date format string which can be overriden for localization support.  The format must be
21258      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21259      */
21260     format : "m/d/y",
21261     /**
21262      * @cfg {String} altFormats
21263      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21264      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21265      */
21266     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21267     
21268     weekStart : 0,
21269     
21270     viewMode : '',
21271     
21272     minViewMode : '',
21273     
21274     todayHighlight : false,
21275     
21276     todayBtn: false,
21277     
21278     language: 'en',
21279     
21280     keyboardNavigation: true,
21281     
21282     calendarWeeks: false,
21283     
21284     startDate: -Infinity,
21285     
21286     endDate: Infinity,
21287     
21288     daysOfWeekDisabled: [],
21289     
21290     _events: [],
21291     
21292     singleMode : false,
21293     
21294     UTCDate: function()
21295     {
21296         return new Date(Date.UTC.apply(Date, arguments));
21297     },
21298     
21299     UTCToday: function()
21300     {
21301         var today = new Date();
21302         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21303     },
21304     
21305     getDate: function() {
21306             var d = this.getUTCDate();
21307             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21308     },
21309     
21310     getUTCDate: function() {
21311             return this.date;
21312     },
21313     
21314     setDate: function(d) {
21315             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21316     },
21317     
21318     setUTCDate: function(d) {
21319             this.date = d;
21320             this.setValue(this.formatDate(this.date));
21321     },
21322         
21323     onRender: function(ct, position)
21324     {
21325         
21326         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21327         
21328         this.language = this.language || 'en';
21329         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21330         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21331         
21332         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21333         this.format = this.format || 'm/d/y';
21334         this.isInline = false;
21335         this.isInput = true;
21336         this.component = this.el.select('.add-on', true).first() || false;
21337         this.component = (this.component && this.component.length === 0) ? false : this.component;
21338         this.hasInput = this.component && this.inputEl().length;
21339         
21340         if (typeof(this.minViewMode === 'string')) {
21341             switch (this.minViewMode) {
21342                 case 'months':
21343                     this.minViewMode = 1;
21344                     break;
21345                 case 'years':
21346                     this.minViewMode = 2;
21347                     break;
21348                 default:
21349                     this.minViewMode = 0;
21350                     break;
21351             }
21352         }
21353         
21354         if (typeof(this.viewMode === 'string')) {
21355             switch (this.viewMode) {
21356                 case 'months':
21357                     this.viewMode = 1;
21358                     break;
21359                 case 'years':
21360                     this.viewMode = 2;
21361                     break;
21362                 default:
21363                     this.viewMode = 0;
21364                     break;
21365             }
21366         }
21367                 
21368         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21369         
21370 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21371         
21372         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21373         
21374         this.picker().on('mousedown', this.onMousedown, this);
21375         this.picker().on('click', this.onClick, this);
21376         
21377         this.picker().addClass('datepicker-dropdown');
21378         
21379         this.startViewMode = this.viewMode;
21380         
21381         if(this.singleMode){
21382             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21383                 v.setVisibilityMode(Roo.Element.DISPLAY);
21384                 v.hide();
21385             });
21386             
21387             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21388                 v.setStyle('width', '189px');
21389             });
21390         }
21391         
21392         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21393             if(!this.calendarWeeks){
21394                 v.remove();
21395                 return;
21396             }
21397             
21398             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21399             v.attr('colspan', function(i, val){
21400                 return parseInt(val) + 1;
21401             });
21402         });
21403                         
21404         
21405         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21406         
21407         this.setStartDate(this.startDate);
21408         this.setEndDate(this.endDate);
21409         
21410         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21411         
21412         this.fillDow();
21413         this.fillMonths();
21414         this.update();
21415         this.showMode();
21416         
21417         if(this.isInline) {
21418             this.showPopup();
21419         }
21420     },
21421     
21422     picker : function()
21423     {
21424         return this.pickerEl;
21425 //        return this.el.select('.datepicker', true).first();
21426     },
21427     
21428     fillDow: function()
21429     {
21430         var dowCnt = this.weekStart;
21431         
21432         var dow = {
21433             tag: 'tr',
21434             cn: [
21435                 
21436             ]
21437         };
21438         
21439         if(this.calendarWeeks){
21440             dow.cn.push({
21441                 tag: 'th',
21442                 cls: 'cw',
21443                 html: '&nbsp;'
21444             })
21445         }
21446         
21447         while (dowCnt < this.weekStart + 7) {
21448             dow.cn.push({
21449                 tag: 'th',
21450                 cls: 'dow',
21451                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21452             });
21453         }
21454         
21455         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21456     },
21457     
21458     fillMonths: function()
21459     {    
21460         var i = 0;
21461         var months = this.picker().select('>.datepicker-months td', true).first();
21462         
21463         months.dom.innerHTML = '';
21464         
21465         while (i < 12) {
21466             var month = {
21467                 tag: 'span',
21468                 cls: 'month',
21469                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21470             };
21471             
21472             months.createChild(month);
21473         }
21474         
21475     },
21476     
21477     update: function()
21478     {
21479         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;
21480         
21481         if (this.date < this.startDate) {
21482             this.viewDate = new Date(this.startDate);
21483         } else if (this.date > this.endDate) {
21484             this.viewDate = new Date(this.endDate);
21485         } else {
21486             this.viewDate = new Date(this.date);
21487         }
21488         
21489         this.fill();
21490     },
21491     
21492     fill: function() 
21493     {
21494         var d = new Date(this.viewDate),
21495                 year = d.getUTCFullYear(),
21496                 month = d.getUTCMonth(),
21497                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21498                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21499                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21500                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21501                 currentDate = this.date && this.date.valueOf(),
21502                 today = this.UTCToday();
21503         
21504         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21505         
21506 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21507         
21508 //        this.picker.select('>tfoot th.today').
21509 //                                              .text(dates[this.language].today)
21510 //                                              .toggle(this.todayBtn !== false);
21511     
21512         this.updateNavArrows();
21513         this.fillMonths();
21514                                                 
21515         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21516         
21517         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21518          
21519         prevMonth.setUTCDate(day);
21520         
21521         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21522         
21523         var nextMonth = new Date(prevMonth);
21524         
21525         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21526         
21527         nextMonth = nextMonth.valueOf();
21528         
21529         var fillMonths = false;
21530         
21531         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21532         
21533         while(prevMonth.valueOf() <= nextMonth) {
21534             var clsName = '';
21535             
21536             if (prevMonth.getUTCDay() === this.weekStart) {
21537                 if(fillMonths){
21538                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21539                 }
21540                     
21541                 fillMonths = {
21542                     tag: 'tr',
21543                     cn: []
21544                 };
21545                 
21546                 if(this.calendarWeeks){
21547                     // ISO 8601: First week contains first thursday.
21548                     // ISO also states week starts on Monday, but we can be more abstract here.
21549                     var
21550                     // Start of current week: based on weekstart/current date
21551                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21552                     // Thursday of this week
21553                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21554                     // First Thursday of year, year from thursday
21555                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21556                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21557                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21558                     
21559                     fillMonths.cn.push({
21560                         tag: 'td',
21561                         cls: 'cw',
21562                         html: calWeek
21563                     });
21564                 }
21565             }
21566             
21567             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21568                 clsName += ' old';
21569             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21570                 clsName += ' new';
21571             }
21572             if (this.todayHighlight &&
21573                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21574                 prevMonth.getUTCMonth() == today.getMonth() &&
21575                 prevMonth.getUTCDate() == today.getDate()) {
21576                 clsName += ' today';
21577             }
21578             
21579             if (currentDate && prevMonth.valueOf() === currentDate) {
21580                 clsName += ' active';
21581             }
21582             
21583             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21584                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21585                     clsName += ' disabled';
21586             }
21587             
21588             fillMonths.cn.push({
21589                 tag: 'td',
21590                 cls: 'day ' + clsName,
21591                 html: prevMonth.getDate()
21592             });
21593             
21594             prevMonth.setDate(prevMonth.getDate()+1);
21595         }
21596           
21597         var currentYear = this.date && this.date.getUTCFullYear();
21598         var currentMonth = this.date && this.date.getUTCMonth();
21599         
21600         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21601         
21602         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21603             v.removeClass('active');
21604             
21605             if(currentYear === year && k === currentMonth){
21606                 v.addClass('active');
21607             }
21608             
21609             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21610                 v.addClass('disabled');
21611             }
21612             
21613         });
21614         
21615         
21616         year = parseInt(year/10, 10) * 10;
21617         
21618         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21619         
21620         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21621         
21622         year -= 1;
21623         for (var i = -1; i < 11; i++) {
21624             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21625                 tag: 'span',
21626                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21627                 html: year
21628             });
21629             
21630             year += 1;
21631         }
21632     },
21633     
21634     showMode: function(dir) 
21635     {
21636         if (dir) {
21637             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21638         }
21639         
21640         Roo.each(this.picker().select('>div',true).elements, function(v){
21641             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21642             v.hide();
21643         });
21644         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21645     },
21646     
21647     place: function()
21648     {
21649         if(this.isInline) {
21650             return;
21651         }
21652         
21653         this.picker().removeClass(['bottom', 'top']);
21654         
21655         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21656             /*
21657              * place to the top of element!
21658              *
21659              */
21660             
21661             this.picker().addClass('top');
21662             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21663             
21664             return;
21665         }
21666         
21667         this.picker().addClass('bottom');
21668         
21669         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21670     },
21671     
21672     parseDate : function(value)
21673     {
21674         if(!value || value instanceof Date){
21675             return value;
21676         }
21677         var v = Date.parseDate(value, this.format);
21678         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21679             v = Date.parseDate(value, 'Y-m-d');
21680         }
21681         if(!v && this.altFormats){
21682             if(!this.altFormatsArray){
21683                 this.altFormatsArray = this.altFormats.split("|");
21684             }
21685             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21686                 v = Date.parseDate(value, this.altFormatsArray[i]);
21687             }
21688         }
21689         return v;
21690     },
21691     
21692     formatDate : function(date, fmt)
21693     {   
21694         return (!date || !(date instanceof Date)) ?
21695         date : date.dateFormat(fmt || this.format);
21696     },
21697     
21698     onFocus : function()
21699     {
21700         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21701         this.showPopup();
21702     },
21703     
21704     onBlur : function()
21705     {
21706         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21707         
21708         var d = this.inputEl().getValue();
21709         
21710         this.setValue(d);
21711                 
21712         this.hidePopup();
21713     },
21714     
21715     showPopup : function()
21716     {
21717         this.picker().show();
21718         this.update();
21719         this.place();
21720         
21721         this.fireEvent('showpopup', this, this.date);
21722     },
21723     
21724     hidePopup : function()
21725     {
21726         if(this.isInline) {
21727             return;
21728         }
21729         this.picker().hide();
21730         this.viewMode = this.startViewMode;
21731         this.showMode();
21732         
21733         this.fireEvent('hidepopup', this, this.date);
21734         
21735     },
21736     
21737     onMousedown: function(e)
21738     {
21739         e.stopPropagation();
21740         e.preventDefault();
21741     },
21742     
21743     keyup: function(e)
21744     {
21745         Roo.bootstrap.DateField.superclass.keyup.call(this);
21746         this.update();
21747     },
21748
21749     setValue: function(v)
21750     {
21751         if(this.fireEvent('beforeselect', this, v) !== false){
21752             var d = new Date(this.parseDate(v) ).clearTime();
21753         
21754             if(isNaN(d.getTime())){
21755                 this.date = this.viewDate = '';
21756                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21757                 return;
21758             }
21759
21760             v = this.formatDate(d);
21761
21762             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21763
21764             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21765
21766             this.update();
21767
21768             this.fireEvent('select', this, this.date);
21769         }
21770     },
21771     
21772     getValue: function()
21773     {
21774         return this.formatDate(this.date);
21775     },
21776     
21777     fireKey: function(e)
21778     {
21779         if (!this.picker().isVisible()){
21780             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21781                 this.showPopup();
21782             }
21783             return;
21784         }
21785         
21786         var dateChanged = false,
21787         dir, day, month,
21788         newDate, newViewDate;
21789         
21790         switch(e.keyCode){
21791             case 27: // escape
21792                 this.hidePopup();
21793                 e.preventDefault();
21794                 break;
21795             case 37: // left
21796             case 39: // right
21797                 if (!this.keyboardNavigation) {
21798                     break;
21799                 }
21800                 dir = e.keyCode == 37 ? -1 : 1;
21801                 
21802                 if (e.ctrlKey){
21803                     newDate = this.moveYear(this.date, dir);
21804                     newViewDate = this.moveYear(this.viewDate, dir);
21805                 } else if (e.shiftKey){
21806                     newDate = this.moveMonth(this.date, dir);
21807                     newViewDate = this.moveMonth(this.viewDate, dir);
21808                 } else {
21809                     newDate = new Date(this.date);
21810                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21811                     newViewDate = new Date(this.viewDate);
21812                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21813                 }
21814                 if (this.dateWithinRange(newDate)){
21815                     this.date = newDate;
21816                     this.viewDate = newViewDate;
21817                     this.setValue(this.formatDate(this.date));
21818 //                    this.update();
21819                     e.preventDefault();
21820                     dateChanged = true;
21821                 }
21822                 break;
21823             case 38: // up
21824             case 40: // down
21825                 if (!this.keyboardNavigation) {
21826                     break;
21827                 }
21828                 dir = e.keyCode == 38 ? -1 : 1;
21829                 if (e.ctrlKey){
21830                     newDate = this.moveYear(this.date, dir);
21831                     newViewDate = this.moveYear(this.viewDate, dir);
21832                 } else if (e.shiftKey){
21833                     newDate = this.moveMonth(this.date, dir);
21834                     newViewDate = this.moveMonth(this.viewDate, dir);
21835                 } else {
21836                     newDate = new Date(this.date);
21837                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21838                     newViewDate = new Date(this.viewDate);
21839                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21840                 }
21841                 if (this.dateWithinRange(newDate)){
21842                     this.date = newDate;
21843                     this.viewDate = newViewDate;
21844                     this.setValue(this.formatDate(this.date));
21845 //                    this.update();
21846                     e.preventDefault();
21847                     dateChanged = true;
21848                 }
21849                 break;
21850             case 13: // enter
21851                 this.setValue(this.formatDate(this.date));
21852                 this.hidePopup();
21853                 e.preventDefault();
21854                 break;
21855             case 9: // tab
21856                 this.setValue(this.formatDate(this.date));
21857                 this.hidePopup();
21858                 break;
21859             case 16: // shift
21860             case 17: // ctrl
21861             case 18: // alt
21862                 break;
21863             default :
21864                 this.hidePopup();
21865                 
21866         }
21867     },
21868     
21869     
21870     onClick: function(e) 
21871     {
21872         e.stopPropagation();
21873         e.preventDefault();
21874         
21875         var target = e.getTarget();
21876         
21877         if(target.nodeName.toLowerCase() === 'i'){
21878             target = Roo.get(target).dom.parentNode;
21879         }
21880         
21881         var nodeName = target.nodeName;
21882         var className = target.className;
21883         var html = target.innerHTML;
21884         //Roo.log(nodeName);
21885         
21886         switch(nodeName.toLowerCase()) {
21887             case 'th':
21888                 switch(className) {
21889                     case 'switch':
21890                         this.showMode(1);
21891                         break;
21892                     case 'prev':
21893                     case 'next':
21894                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21895                         switch(this.viewMode){
21896                                 case 0:
21897                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21898                                         break;
21899                                 case 1:
21900                                 case 2:
21901                                         this.viewDate = this.moveYear(this.viewDate, dir);
21902                                         break;
21903                         }
21904                         this.fill();
21905                         break;
21906                     case 'today':
21907                         var date = new Date();
21908                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21909 //                        this.fill()
21910                         this.setValue(this.formatDate(this.date));
21911                         
21912                         this.hidePopup();
21913                         break;
21914                 }
21915                 break;
21916             case 'span':
21917                 if (className.indexOf('disabled') < 0) {
21918                     this.viewDate.setUTCDate(1);
21919                     if (className.indexOf('month') > -1) {
21920                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21921                     } else {
21922                         var year = parseInt(html, 10) || 0;
21923                         this.viewDate.setUTCFullYear(year);
21924                         
21925                     }
21926                     
21927                     if(this.singleMode){
21928                         this.setValue(this.formatDate(this.viewDate));
21929                         this.hidePopup();
21930                         return;
21931                     }
21932                     
21933                     this.showMode(-1);
21934                     this.fill();
21935                 }
21936                 break;
21937                 
21938             case 'td':
21939                 //Roo.log(className);
21940                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21941                     var day = parseInt(html, 10) || 1;
21942                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21943                         month = (this.viewDate || new Date()).getUTCMonth();
21944
21945                     if (className.indexOf('old') > -1) {
21946                         if(month === 0 ){
21947                             month = 11;
21948                             year -= 1;
21949                         }else{
21950                             month -= 1;
21951                         }
21952                     } else if (className.indexOf('new') > -1) {
21953                         if (month == 11) {
21954                             month = 0;
21955                             year += 1;
21956                         } else {
21957                             month += 1;
21958                         }
21959                     }
21960                     //Roo.log([year,month,day]);
21961                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21962                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21963 //                    this.fill();
21964                     //Roo.log(this.formatDate(this.date));
21965                     this.setValue(this.formatDate(this.date));
21966                     this.hidePopup();
21967                 }
21968                 break;
21969         }
21970     },
21971     
21972     setStartDate: function(startDate)
21973     {
21974         this.startDate = startDate || -Infinity;
21975         if (this.startDate !== -Infinity) {
21976             this.startDate = this.parseDate(this.startDate);
21977         }
21978         this.update();
21979         this.updateNavArrows();
21980     },
21981
21982     setEndDate: function(endDate)
21983     {
21984         this.endDate = endDate || Infinity;
21985         if (this.endDate !== Infinity) {
21986             this.endDate = this.parseDate(this.endDate);
21987         }
21988         this.update();
21989         this.updateNavArrows();
21990     },
21991     
21992     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21993     {
21994         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21995         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21996             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21997         }
21998         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21999             return parseInt(d, 10);
22000         });
22001         this.update();
22002         this.updateNavArrows();
22003     },
22004     
22005     updateNavArrows: function() 
22006     {
22007         if(this.singleMode){
22008             return;
22009         }
22010         
22011         var d = new Date(this.viewDate),
22012         year = d.getUTCFullYear(),
22013         month = d.getUTCMonth();
22014         
22015         Roo.each(this.picker().select('.prev', true).elements, function(v){
22016             v.show();
22017             switch (this.viewMode) {
22018                 case 0:
22019
22020                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22021                         v.hide();
22022                     }
22023                     break;
22024                 case 1:
22025                 case 2:
22026                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22027                         v.hide();
22028                     }
22029                     break;
22030             }
22031         });
22032         
22033         Roo.each(this.picker().select('.next', true).elements, function(v){
22034             v.show();
22035             switch (this.viewMode) {
22036                 case 0:
22037
22038                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22039                         v.hide();
22040                     }
22041                     break;
22042                 case 1:
22043                 case 2:
22044                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22045                         v.hide();
22046                     }
22047                     break;
22048             }
22049         })
22050     },
22051     
22052     moveMonth: function(date, dir)
22053     {
22054         if (!dir) {
22055             return date;
22056         }
22057         var new_date = new Date(date.valueOf()),
22058         day = new_date.getUTCDate(),
22059         month = new_date.getUTCMonth(),
22060         mag = Math.abs(dir),
22061         new_month, test;
22062         dir = dir > 0 ? 1 : -1;
22063         if (mag == 1){
22064             test = dir == -1
22065             // If going back one month, make sure month is not current month
22066             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22067             ? function(){
22068                 return new_date.getUTCMonth() == month;
22069             }
22070             // If going forward one month, make sure month is as expected
22071             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22072             : function(){
22073                 return new_date.getUTCMonth() != new_month;
22074             };
22075             new_month = month + dir;
22076             new_date.setUTCMonth(new_month);
22077             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22078             if (new_month < 0 || new_month > 11) {
22079                 new_month = (new_month + 12) % 12;
22080             }
22081         } else {
22082             // For magnitudes >1, move one month at a time...
22083             for (var i=0; i<mag; i++) {
22084                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22085                 new_date = this.moveMonth(new_date, dir);
22086             }
22087             // ...then reset the day, keeping it in the new month
22088             new_month = new_date.getUTCMonth();
22089             new_date.setUTCDate(day);
22090             test = function(){
22091                 return new_month != new_date.getUTCMonth();
22092             };
22093         }
22094         // Common date-resetting loop -- if date is beyond end of month, make it
22095         // end of month
22096         while (test()){
22097             new_date.setUTCDate(--day);
22098             new_date.setUTCMonth(new_month);
22099         }
22100         return new_date;
22101     },
22102
22103     moveYear: function(date, dir)
22104     {
22105         return this.moveMonth(date, dir*12);
22106     },
22107
22108     dateWithinRange: function(date)
22109     {
22110         return date >= this.startDate && date <= this.endDate;
22111     },
22112
22113     
22114     remove: function() 
22115     {
22116         this.picker().remove();
22117     },
22118     
22119     validateValue : function(value)
22120     {
22121         if(this.getVisibilityEl().hasClass('hidden')){
22122             return true;
22123         }
22124         
22125         if(value.length < 1)  {
22126             if(this.allowBlank){
22127                 return true;
22128             }
22129             return false;
22130         }
22131         
22132         if(value.length < this.minLength){
22133             return false;
22134         }
22135         if(value.length > this.maxLength){
22136             return false;
22137         }
22138         if(this.vtype){
22139             var vt = Roo.form.VTypes;
22140             if(!vt[this.vtype](value, this)){
22141                 return false;
22142             }
22143         }
22144         if(typeof this.validator == "function"){
22145             var msg = this.validator(value);
22146             if(msg !== true){
22147                 return false;
22148             }
22149         }
22150         
22151         if(this.regex && !this.regex.test(value)){
22152             return false;
22153         }
22154         
22155         if(typeof(this.parseDate(value)) == 'undefined'){
22156             return false;
22157         }
22158         
22159         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22160             return false;
22161         }      
22162         
22163         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22164             return false;
22165         } 
22166         
22167         
22168         return true;
22169     },
22170     
22171     reset : function()
22172     {
22173         this.date = this.viewDate = '';
22174         
22175         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22176     }
22177    
22178 });
22179
22180 Roo.apply(Roo.bootstrap.DateField,  {
22181     
22182     head : {
22183         tag: 'thead',
22184         cn: [
22185         {
22186             tag: 'tr',
22187             cn: [
22188             {
22189                 tag: 'th',
22190                 cls: 'prev',
22191                 html: '<i class="fa fa-arrow-left"/>'
22192             },
22193             {
22194                 tag: 'th',
22195                 cls: 'switch',
22196                 colspan: '5'
22197             },
22198             {
22199                 tag: 'th',
22200                 cls: 'next',
22201                 html: '<i class="fa fa-arrow-right"/>'
22202             }
22203
22204             ]
22205         }
22206         ]
22207     },
22208     
22209     content : {
22210         tag: 'tbody',
22211         cn: [
22212         {
22213             tag: 'tr',
22214             cn: [
22215             {
22216                 tag: 'td',
22217                 colspan: '7'
22218             }
22219             ]
22220         }
22221         ]
22222     },
22223     
22224     footer : {
22225         tag: 'tfoot',
22226         cn: [
22227         {
22228             tag: 'tr',
22229             cn: [
22230             {
22231                 tag: 'th',
22232                 colspan: '7',
22233                 cls: 'today'
22234             }
22235                     
22236             ]
22237         }
22238         ]
22239     },
22240     
22241     dates:{
22242         en: {
22243             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22244             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22245             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22246             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22247             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22248             today: "Today"
22249         }
22250     },
22251     
22252     modes: [
22253     {
22254         clsName: 'days',
22255         navFnc: 'Month',
22256         navStep: 1
22257     },
22258     {
22259         clsName: 'months',
22260         navFnc: 'FullYear',
22261         navStep: 1
22262     },
22263     {
22264         clsName: 'years',
22265         navFnc: 'FullYear',
22266         navStep: 10
22267     }]
22268 });
22269
22270 Roo.apply(Roo.bootstrap.DateField,  {
22271   
22272     template : {
22273         tag: 'div',
22274         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22275         cn: [
22276         {
22277             tag: 'div',
22278             cls: 'datepicker-days',
22279             cn: [
22280             {
22281                 tag: 'table',
22282                 cls: 'table-condensed',
22283                 cn:[
22284                 Roo.bootstrap.DateField.head,
22285                 {
22286                     tag: 'tbody'
22287                 },
22288                 Roo.bootstrap.DateField.footer
22289                 ]
22290             }
22291             ]
22292         },
22293         {
22294             tag: 'div',
22295             cls: 'datepicker-months',
22296             cn: [
22297             {
22298                 tag: 'table',
22299                 cls: 'table-condensed',
22300                 cn:[
22301                 Roo.bootstrap.DateField.head,
22302                 Roo.bootstrap.DateField.content,
22303                 Roo.bootstrap.DateField.footer
22304                 ]
22305             }
22306             ]
22307         },
22308         {
22309             tag: 'div',
22310             cls: 'datepicker-years',
22311             cn: [
22312             {
22313                 tag: 'table',
22314                 cls: 'table-condensed',
22315                 cn:[
22316                 Roo.bootstrap.DateField.head,
22317                 Roo.bootstrap.DateField.content,
22318                 Roo.bootstrap.DateField.footer
22319                 ]
22320             }
22321             ]
22322         }
22323         ]
22324     }
22325 });
22326
22327  
22328
22329  /*
22330  * - LGPL
22331  *
22332  * TimeField
22333  * 
22334  */
22335
22336 /**
22337  * @class Roo.bootstrap.TimeField
22338  * @extends Roo.bootstrap.Input
22339  * Bootstrap DateField class
22340  * 
22341  * 
22342  * @constructor
22343  * Create a new TimeField
22344  * @param {Object} config The config object
22345  */
22346
22347 Roo.bootstrap.TimeField = function(config){
22348     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22349     this.addEvents({
22350             /**
22351              * @event show
22352              * Fires when this field show.
22353              * @param {Roo.bootstrap.DateField} thisthis
22354              * @param {Mixed} date The date value
22355              */
22356             show : true,
22357             /**
22358              * @event show
22359              * Fires when this field hide.
22360              * @param {Roo.bootstrap.DateField} this
22361              * @param {Mixed} date The date value
22362              */
22363             hide : true,
22364             /**
22365              * @event select
22366              * Fires when select a date.
22367              * @param {Roo.bootstrap.DateField} this
22368              * @param {Mixed} date The date value
22369              */
22370             select : true
22371         });
22372 };
22373
22374 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22375     
22376     /**
22377      * @cfg {String} format
22378      * The default time format string which can be overriden for localization support.  The format must be
22379      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22380      */
22381     format : "H:i",
22382
22383     getAutoCreate : function()
22384     {
22385         this.after = '<i class="fa far fa-clock"></i>';
22386         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22387         
22388          
22389     },
22390     onRender: function(ct, position)
22391     {
22392         
22393         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22394                 
22395         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22396         
22397         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22398         
22399         this.pop = this.picker().select('>.datepicker-time',true).first();
22400         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22401         
22402         this.picker().on('mousedown', this.onMousedown, this);
22403         this.picker().on('click', this.onClick, this);
22404         
22405         this.picker().addClass('datepicker-dropdown');
22406     
22407         this.fillTime();
22408         this.update();
22409             
22410         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22411         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22412         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22413         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22414         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22415         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22416
22417     },
22418     
22419     fireKey: function(e){
22420         if (!this.picker().isVisible()){
22421             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22422                 this.show();
22423             }
22424             return;
22425         }
22426
22427         e.preventDefault();
22428         
22429         switch(e.keyCode){
22430             case 27: // escape
22431                 this.hide();
22432                 break;
22433             case 37: // left
22434             case 39: // right
22435                 this.onTogglePeriod();
22436                 break;
22437             case 38: // up
22438                 this.onIncrementMinutes();
22439                 break;
22440             case 40: // down
22441                 this.onDecrementMinutes();
22442                 break;
22443             case 13: // enter
22444             case 9: // tab
22445                 this.setTime();
22446                 break;
22447         }
22448     },
22449     
22450     onClick: function(e) {
22451         e.stopPropagation();
22452         e.preventDefault();
22453     },
22454     
22455     picker : function()
22456     {
22457         return this.pickerEl;
22458     },
22459     
22460     fillTime: function()
22461     {    
22462         var time = this.pop.select('tbody', true).first();
22463         
22464         time.dom.innerHTML = '';
22465         
22466         time.createChild({
22467             tag: 'tr',
22468             cn: [
22469                 {
22470                     tag: 'td',
22471                     cn: [
22472                         {
22473                             tag: 'a',
22474                             href: '#',
22475                             cls: 'btn',
22476                             cn: [
22477                                 {
22478                                     tag: 'i',
22479                                     cls: 'hours-up fa fas fa-chevron-up'
22480                                 }
22481                             ]
22482                         } 
22483                     ]
22484                 },
22485                 {
22486                     tag: 'td',
22487                     cls: 'separator'
22488                 },
22489                 {
22490                     tag: 'td',
22491                     cn: [
22492                         {
22493                             tag: 'a',
22494                             href: '#',
22495                             cls: 'btn',
22496                             cn: [
22497                                 {
22498                                     tag: 'i',
22499                                     cls: 'minutes-up fa fas fa-chevron-up'
22500                                 }
22501                             ]
22502                         }
22503                     ]
22504                 },
22505                 {
22506                     tag: 'td',
22507                     cls: 'separator'
22508                 }
22509             ]
22510         });
22511         
22512         time.createChild({
22513             tag: 'tr',
22514             cn: [
22515                 {
22516                     tag: 'td',
22517                     cn: [
22518                         {
22519                             tag: 'span',
22520                             cls: 'timepicker-hour',
22521                             html: '00'
22522                         }  
22523                     ]
22524                 },
22525                 {
22526                     tag: 'td',
22527                     cls: 'separator',
22528                     html: ':'
22529                 },
22530                 {
22531                     tag: 'td',
22532                     cn: [
22533                         {
22534                             tag: 'span',
22535                             cls: 'timepicker-minute',
22536                             html: '00'
22537                         }  
22538                     ]
22539                 },
22540                 {
22541                     tag: 'td',
22542                     cls: 'separator'
22543                 },
22544                 {
22545                     tag: 'td',
22546                     cn: [
22547                         {
22548                             tag: 'button',
22549                             type: 'button',
22550                             cls: 'btn btn-primary period',
22551                             html: 'AM'
22552                             
22553                         }
22554                     ]
22555                 }
22556             ]
22557         });
22558         
22559         time.createChild({
22560             tag: 'tr',
22561             cn: [
22562                 {
22563                     tag: 'td',
22564                     cn: [
22565                         {
22566                             tag: 'a',
22567                             href: '#',
22568                             cls: 'btn',
22569                             cn: [
22570                                 {
22571                                     tag: 'span',
22572                                     cls: 'hours-down fa fas fa-chevron-down'
22573                                 }
22574                             ]
22575                         }
22576                     ]
22577                 },
22578                 {
22579                     tag: 'td',
22580                     cls: 'separator'
22581                 },
22582                 {
22583                     tag: 'td',
22584                     cn: [
22585                         {
22586                             tag: 'a',
22587                             href: '#',
22588                             cls: 'btn',
22589                             cn: [
22590                                 {
22591                                     tag: 'span',
22592                                     cls: 'minutes-down fa fas fa-chevron-down'
22593                                 }
22594                             ]
22595                         }
22596                     ]
22597                 },
22598                 {
22599                     tag: 'td',
22600                     cls: 'separator'
22601                 }
22602             ]
22603         });
22604         
22605     },
22606     
22607     update: function()
22608     {
22609         
22610         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22611         
22612         this.fill();
22613     },
22614     
22615     fill: function() 
22616     {
22617         var hours = this.time.getHours();
22618         var minutes = this.time.getMinutes();
22619         var period = 'AM';
22620         
22621         if(hours > 11){
22622             period = 'PM';
22623         }
22624         
22625         if(hours == 0){
22626             hours = 12;
22627         }
22628         
22629         
22630         if(hours > 12){
22631             hours = hours - 12;
22632         }
22633         
22634         if(hours < 10){
22635             hours = '0' + hours;
22636         }
22637         
22638         if(minutes < 10){
22639             minutes = '0' + minutes;
22640         }
22641         
22642         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22643         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22644         this.pop.select('button', true).first().dom.innerHTML = period;
22645         
22646     },
22647     
22648     place: function()
22649     {   
22650         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22651         
22652         var cls = ['bottom'];
22653         
22654         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22655             cls.pop();
22656             cls.push('top');
22657         }
22658         
22659         cls.push('right');
22660         
22661         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22662             cls.pop();
22663             cls.push('left');
22664         }
22665         //this.picker().setXY(20000,20000);
22666         this.picker().addClass(cls.join('-'));
22667         
22668         var _this = this;
22669         
22670         Roo.each(cls, function(c){
22671             if(c == 'bottom'){
22672                 (function() {
22673                  //  
22674                 }).defer(200);
22675                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22676                 //_this.picker().setTop(_this.inputEl().getHeight());
22677                 return;
22678             }
22679             if(c == 'top'){
22680                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22681                 
22682                 //_this.picker().setTop(0 - _this.picker().getHeight());
22683                 return;
22684             }
22685             /*
22686             if(c == 'left'){
22687                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22688                 return;
22689             }
22690             if(c == 'right'){
22691                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22692                 return;
22693             }
22694             */
22695         });
22696         
22697     },
22698   
22699     onFocus : function()
22700     {
22701         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22702         this.show();
22703     },
22704     
22705     onBlur : function()
22706     {
22707         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22708         this.hide();
22709     },
22710     
22711     show : function()
22712     {
22713         this.picker().show();
22714         this.pop.show();
22715         this.update();
22716         this.place();
22717         
22718         this.fireEvent('show', this, this.date);
22719     },
22720     
22721     hide : function()
22722     {
22723         this.picker().hide();
22724         this.pop.hide();
22725         
22726         this.fireEvent('hide', this, this.date);
22727     },
22728     
22729     setTime : function()
22730     {
22731         this.hide();
22732         this.setValue(this.time.format(this.format));
22733         
22734         this.fireEvent('select', this, this.date);
22735         
22736         
22737     },
22738     
22739     onMousedown: function(e){
22740         e.stopPropagation();
22741         e.preventDefault();
22742     },
22743     
22744     onIncrementHours: function()
22745     {
22746         Roo.log('onIncrementHours');
22747         this.time = this.time.add(Date.HOUR, 1);
22748         this.update();
22749         
22750     },
22751     
22752     onDecrementHours: function()
22753     {
22754         Roo.log('onDecrementHours');
22755         this.time = this.time.add(Date.HOUR, -1);
22756         this.update();
22757     },
22758     
22759     onIncrementMinutes: function()
22760     {
22761         Roo.log('onIncrementMinutes');
22762         this.time = this.time.add(Date.MINUTE, 1);
22763         this.update();
22764     },
22765     
22766     onDecrementMinutes: function()
22767     {
22768         Roo.log('onDecrementMinutes');
22769         this.time = this.time.add(Date.MINUTE, -1);
22770         this.update();
22771     },
22772     
22773     onTogglePeriod: function()
22774     {
22775         Roo.log('onTogglePeriod');
22776         this.time = this.time.add(Date.HOUR, 12);
22777         this.update();
22778     }
22779     
22780    
22781 });
22782  
22783
22784 Roo.apply(Roo.bootstrap.TimeField,  {
22785   
22786     template : {
22787         tag: 'div',
22788         cls: 'datepicker dropdown-menu',
22789         cn: [
22790             {
22791                 tag: 'div',
22792                 cls: 'datepicker-time',
22793                 cn: [
22794                 {
22795                     tag: 'table',
22796                     cls: 'table-condensed',
22797                     cn:[
22798                         {
22799                             tag: 'tbody',
22800                             cn: [
22801                                 {
22802                                     tag: 'tr',
22803                                     cn: [
22804                                     {
22805                                         tag: 'td',
22806                                         colspan: '7'
22807                                     }
22808                                     ]
22809                                 }
22810                             ]
22811                         },
22812                         {
22813                             tag: 'tfoot',
22814                             cn: [
22815                                 {
22816                                     tag: 'tr',
22817                                     cn: [
22818                                     {
22819                                         tag: 'th',
22820                                         colspan: '7',
22821                                         cls: '',
22822                                         cn: [
22823                                             {
22824                                                 tag: 'button',
22825                                                 cls: 'btn btn-info ok',
22826                                                 html: 'OK'
22827                                             }
22828                                         ]
22829                                     }
22830                     
22831                                     ]
22832                                 }
22833                             ]
22834                         }
22835                     ]
22836                 }
22837                 ]
22838             }
22839         ]
22840     }
22841 });
22842
22843  
22844
22845  /*
22846  * - LGPL
22847  *
22848  * MonthField
22849  * 
22850  */
22851
22852 /**
22853  * @class Roo.bootstrap.MonthField
22854  * @extends Roo.bootstrap.Input
22855  * Bootstrap MonthField class
22856  * 
22857  * @cfg {String} language default en
22858  * 
22859  * @constructor
22860  * Create a new MonthField
22861  * @param {Object} config The config object
22862  */
22863
22864 Roo.bootstrap.MonthField = function(config){
22865     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22866     
22867     this.addEvents({
22868         /**
22869          * @event show
22870          * Fires when this field show.
22871          * @param {Roo.bootstrap.MonthField} this
22872          * @param {Mixed} date The date value
22873          */
22874         show : true,
22875         /**
22876          * @event show
22877          * Fires when this field hide.
22878          * @param {Roo.bootstrap.MonthField} this
22879          * @param {Mixed} date The date value
22880          */
22881         hide : true,
22882         /**
22883          * @event select
22884          * Fires when select a date.
22885          * @param {Roo.bootstrap.MonthField} this
22886          * @param {String} oldvalue The old value
22887          * @param {String} newvalue The new value
22888          */
22889         select : true
22890     });
22891 };
22892
22893 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22894     
22895     onRender: function(ct, position)
22896     {
22897         
22898         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22899         
22900         this.language = this.language || 'en';
22901         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22902         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22903         
22904         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22905         this.isInline = false;
22906         this.isInput = true;
22907         this.component = this.el.select('.add-on', true).first() || false;
22908         this.component = (this.component && this.component.length === 0) ? false : this.component;
22909         this.hasInput = this.component && this.inputEL().length;
22910         
22911         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22912         
22913         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22914         
22915         this.picker().on('mousedown', this.onMousedown, this);
22916         this.picker().on('click', this.onClick, this);
22917         
22918         this.picker().addClass('datepicker-dropdown');
22919         
22920         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22921             v.setStyle('width', '189px');
22922         });
22923         
22924         this.fillMonths();
22925         
22926         this.update();
22927         
22928         if(this.isInline) {
22929             this.show();
22930         }
22931         
22932     },
22933     
22934     setValue: function(v, suppressEvent)
22935     {   
22936         var o = this.getValue();
22937         
22938         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22939         
22940         this.update();
22941
22942         if(suppressEvent !== true){
22943             this.fireEvent('select', this, o, v);
22944         }
22945         
22946     },
22947     
22948     getValue: function()
22949     {
22950         return this.value;
22951     },
22952     
22953     onClick: function(e) 
22954     {
22955         e.stopPropagation();
22956         e.preventDefault();
22957         
22958         var target = e.getTarget();
22959         
22960         if(target.nodeName.toLowerCase() === 'i'){
22961             target = Roo.get(target).dom.parentNode;
22962         }
22963         
22964         var nodeName = target.nodeName;
22965         var className = target.className;
22966         var html = target.innerHTML;
22967         
22968         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22969             return;
22970         }
22971         
22972         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22973         
22974         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22975         
22976         this.hide();
22977                         
22978     },
22979     
22980     picker : function()
22981     {
22982         return this.pickerEl;
22983     },
22984     
22985     fillMonths: function()
22986     {    
22987         var i = 0;
22988         var months = this.picker().select('>.datepicker-months td', true).first();
22989         
22990         months.dom.innerHTML = '';
22991         
22992         while (i < 12) {
22993             var month = {
22994                 tag: 'span',
22995                 cls: 'month',
22996                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22997             };
22998             
22999             months.createChild(month);
23000         }
23001         
23002     },
23003     
23004     update: function()
23005     {
23006         var _this = this;
23007         
23008         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23009             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23010         }
23011         
23012         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23013             e.removeClass('active');
23014             
23015             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23016                 e.addClass('active');
23017             }
23018         })
23019     },
23020     
23021     place: function()
23022     {
23023         if(this.isInline) {
23024             return;
23025         }
23026         
23027         this.picker().removeClass(['bottom', 'top']);
23028         
23029         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23030             /*
23031              * place to the top of element!
23032              *
23033              */
23034             
23035             this.picker().addClass('top');
23036             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23037             
23038             return;
23039         }
23040         
23041         this.picker().addClass('bottom');
23042         
23043         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23044     },
23045     
23046     onFocus : function()
23047     {
23048         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23049         this.show();
23050     },
23051     
23052     onBlur : function()
23053     {
23054         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23055         
23056         var d = this.inputEl().getValue();
23057         
23058         this.setValue(d);
23059                 
23060         this.hide();
23061     },
23062     
23063     show : function()
23064     {
23065         this.picker().show();
23066         this.picker().select('>.datepicker-months', true).first().show();
23067         this.update();
23068         this.place();
23069         
23070         this.fireEvent('show', this, this.date);
23071     },
23072     
23073     hide : function()
23074     {
23075         if(this.isInline) {
23076             return;
23077         }
23078         this.picker().hide();
23079         this.fireEvent('hide', this, this.date);
23080         
23081     },
23082     
23083     onMousedown: function(e)
23084     {
23085         e.stopPropagation();
23086         e.preventDefault();
23087     },
23088     
23089     keyup: function(e)
23090     {
23091         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23092         this.update();
23093     },
23094
23095     fireKey: function(e)
23096     {
23097         if (!this.picker().isVisible()){
23098             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23099                 this.show();
23100             }
23101             return;
23102         }
23103         
23104         var dir;
23105         
23106         switch(e.keyCode){
23107             case 27: // escape
23108                 this.hide();
23109                 e.preventDefault();
23110                 break;
23111             case 37: // left
23112             case 39: // right
23113                 dir = e.keyCode == 37 ? -1 : 1;
23114                 
23115                 this.vIndex = this.vIndex + dir;
23116                 
23117                 if(this.vIndex < 0){
23118                     this.vIndex = 0;
23119                 }
23120                 
23121                 if(this.vIndex > 11){
23122                     this.vIndex = 11;
23123                 }
23124                 
23125                 if(isNaN(this.vIndex)){
23126                     this.vIndex = 0;
23127                 }
23128                 
23129                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23130                 
23131                 break;
23132             case 38: // up
23133             case 40: // down
23134                 
23135                 dir = e.keyCode == 38 ? -1 : 1;
23136                 
23137                 this.vIndex = this.vIndex + dir * 4;
23138                 
23139                 if(this.vIndex < 0){
23140                     this.vIndex = 0;
23141                 }
23142                 
23143                 if(this.vIndex > 11){
23144                     this.vIndex = 11;
23145                 }
23146                 
23147                 if(isNaN(this.vIndex)){
23148                     this.vIndex = 0;
23149                 }
23150                 
23151                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23152                 break;
23153                 
23154             case 13: // enter
23155                 
23156                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23157                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23158                 }
23159                 
23160                 this.hide();
23161                 e.preventDefault();
23162                 break;
23163             case 9: // tab
23164                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23165                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23166                 }
23167                 this.hide();
23168                 break;
23169             case 16: // shift
23170             case 17: // ctrl
23171             case 18: // alt
23172                 break;
23173             default :
23174                 this.hide();
23175                 
23176         }
23177     },
23178     
23179     remove: function() 
23180     {
23181         this.picker().remove();
23182     }
23183    
23184 });
23185
23186 Roo.apply(Roo.bootstrap.MonthField,  {
23187     
23188     content : {
23189         tag: 'tbody',
23190         cn: [
23191         {
23192             tag: 'tr',
23193             cn: [
23194             {
23195                 tag: 'td',
23196                 colspan: '7'
23197             }
23198             ]
23199         }
23200         ]
23201     },
23202     
23203     dates:{
23204         en: {
23205             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23206             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23207         }
23208     }
23209 });
23210
23211 Roo.apply(Roo.bootstrap.MonthField,  {
23212   
23213     template : {
23214         tag: 'div',
23215         cls: 'datepicker dropdown-menu roo-dynamic',
23216         cn: [
23217             {
23218                 tag: 'div',
23219                 cls: 'datepicker-months',
23220                 cn: [
23221                 {
23222                     tag: 'table',
23223                     cls: 'table-condensed',
23224                     cn:[
23225                         Roo.bootstrap.DateField.content
23226                     ]
23227                 }
23228                 ]
23229             }
23230         ]
23231     }
23232 });
23233
23234  
23235
23236  
23237  /*
23238  * - LGPL
23239  *
23240  * CheckBox
23241  * 
23242  */
23243
23244 /**
23245  * @class Roo.bootstrap.CheckBox
23246  * @extends Roo.bootstrap.Input
23247  * Bootstrap CheckBox class
23248  * 
23249  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23250  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23251  * @cfg {String} boxLabel The text that appears beside the checkbox
23252  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23253  * @cfg {Boolean} checked initnal the element
23254  * @cfg {Boolean} inline inline the element (default false)
23255  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23256  * @cfg {String} tooltip label tooltip
23257  * 
23258  * @constructor
23259  * Create a new CheckBox
23260  * @param {Object} config The config object
23261  */
23262
23263 Roo.bootstrap.CheckBox = function(config){
23264     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23265    
23266     this.addEvents({
23267         /**
23268         * @event check
23269         * Fires when the element is checked or unchecked.
23270         * @param {Roo.bootstrap.CheckBox} this This input
23271         * @param {Boolean} checked The new checked value
23272         */
23273        check : true,
23274        /**
23275         * @event click
23276         * Fires when the element is click.
23277         * @param {Roo.bootstrap.CheckBox} this This input
23278         */
23279        click : true
23280     });
23281     
23282 };
23283
23284 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23285   
23286     inputType: 'checkbox',
23287     inputValue: 1,
23288     valueOff: 0,
23289     boxLabel: false,
23290     checked: false,
23291     weight : false,
23292     inline: false,
23293     tooltip : '',
23294     
23295     // checkbox success does not make any sense really.. 
23296     invalidClass : "",
23297     validClass : "",
23298     
23299     
23300     getAutoCreate : function()
23301     {
23302         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23303         
23304         var id = Roo.id();
23305         
23306         var cfg = {};
23307         
23308         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23309         
23310         if(this.inline){
23311             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23312         }
23313         
23314         var input =  {
23315             tag: 'input',
23316             id : id,
23317             type : this.inputType,
23318             value : this.inputValue,
23319             cls : 'roo-' + this.inputType, //'form-box',
23320             placeholder : this.placeholder || ''
23321             
23322         };
23323         
23324         if(this.inputType != 'radio'){
23325             var hidden =  {
23326                 tag: 'input',
23327                 type : 'hidden',
23328                 cls : 'roo-hidden-value',
23329                 value : this.checked ? this.inputValue : this.valueOff
23330             };
23331         }
23332         
23333             
23334         if (this.weight) { // Validity check?
23335             cfg.cls += " " + this.inputType + "-" + this.weight;
23336         }
23337         
23338         if (this.disabled) {
23339             input.disabled=true;
23340         }
23341         
23342         if(this.checked){
23343             input.checked = this.checked;
23344         }
23345         
23346         if (this.name) {
23347             
23348             input.name = this.name;
23349             
23350             if(this.inputType != 'radio'){
23351                 hidden.name = this.name;
23352                 input.name = '_hidden_' + this.name;
23353             }
23354         }
23355         
23356         if (this.size) {
23357             input.cls += ' input-' + this.size;
23358         }
23359         
23360         var settings=this;
23361         
23362         ['xs','sm','md','lg'].map(function(size){
23363             if (settings[size]) {
23364                 cfg.cls += ' col-' + size + '-' + settings[size];
23365             }
23366         });
23367         
23368         var inputblock = input;
23369          
23370         if (this.before || this.after) {
23371             
23372             inputblock = {
23373                 cls : 'input-group',
23374                 cn :  [] 
23375             };
23376             
23377             if (this.before) {
23378                 inputblock.cn.push({
23379                     tag :'span',
23380                     cls : 'input-group-addon',
23381                     html : this.before
23382                 });
23383             }
23384             
23385             inputblock.cn.push(input);
23386             
23387             if(this.inputType != 'radio'){
23388                 inputblock.cn.push(hidden);
23389             }
23390             
23391             if (this.after) {
23392                 inputblock.cn.push({
23393                     tag :'span',
23394                     cls : 'input-group-addon',
23395                     html : this.after
23396                 });
23397             }
23398             
23399         }
23400         var boxLabelCfg = false;
23401         
23402         if(this.boxLabel){
23403            
23404             boxLabelCfg = {
23405                 tag: 'label',
23406                 //'for': id, // box label is handled by onclick - so no for...
23407                 cls: 'box-label',
23408                 html: this.boxLabel
23409             };
23410             if(this.tooltip){
23411                 boxLabelCfg.tooltip = this.tooltip;
23412             }
23413              
23414         }
23415         
23416         
23417         if (align ==='left' && this.fieldLabel.length) {
23418 //                Roo.log("left and has label");
23419             cfg.cn = [
23420                 {
23421                     tag: 'label',
23422                     'for' :  id,
23423                     cls : 'control-label',
23424                     html : this.fieldLabel
23425                 },
23426                 {
23427                     cls : "", 
23428                     cn: [
23429                         inputblock
23430                     ]
23431                 }
23432             ];
23433             
23434             if (boxLabelCfg) {
23435                 cfg.cn[1].cn.push(boxLabelCfg);
23436             }
23437             
23438             if(this.labelWidth > 12){
23439                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23440             }
23441             
23442             if(this.labelWidth < 13 && this.labelmd == 0){
23443                 this.labelmd = this.labelWidth;
23444             }
23445             
23446             if(this.labellg > 0){
23447                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23448                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23449             }
23450             
23451             if(this.labelmd > 0){
23452                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23453                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23454             }
23455             
23456             if(this.labelsm > 0){
23457                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23458                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23459             }
23460             
23461             if(this.labelxs > 0){
23462                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23463                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23464             }
23465             
23466         } else if ( this.fieldLabel.length) {
23467 //                Roo.log(" label");
23468                 cfg.cn = [
23469                    
23470                     {
23471                         tag: this.boxLabel ? 'span' : 'label',
23472                         'for': id,
23473                         cls: 'control-label box-input-label',
23474                         //cls : 'input-group-addon',
23475                         html : this.fieldLabel
23476                     },
23477                     
23478                     inputblock
23479                     
23480                 ];
23481                 if (boxLabelCfg) {
23482                     cfg.cn.push(boxLabelCfg);
23483                 }
23484
23485         } else {
23486             
23487 //                Roo.log(" no label && no align");
23488                 cfg.cn = [  inputblock ] ;
23489                 if (boxLabelCfg) {
23490                     cfg.cn.push(boxLabelCfg);
23491                 }
23492
23493                 
23494         }
23495         
23496        
23497         
23498         if(this.inputType != 'radio'){
23499             cfg.cn.push(hidden);
23500         }
23501         
23502         return cfg;
23503         
23504     },
23505     
23506     /**
23507      * return the real input element.
23508      */
23509     inputEl: function ()
23510     {
23511         return this.el.select('input.roo-' + this.inputType,true).first();
23512     },
23513     hiddenEl: function ()
23514     {
23515         return this.el.select('input.roo-hidden-value',true).first();
23516     },
23517     
23518     labelEl: function()
23519     {
23520         return this.el.select('label.control-label',true).first();
23521     },
23522     /* depricated... */
23523     
23524     label: function()
23525     {
23526         return this.labelEl();
23527     },
23528     
23529     boxLabelEl: function()
23530     {
23531         return this.el.select('label.box-label',true).first();
23532     },
23533     
23534     initEvents : function()
23535     {
23536 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23537         
23538         this.inputEl().on('click', this.onClick,  this);
23539         
23540         if (this.boxLabel) { 
23541             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23542         }
23543         
23544         this.startValue = this.getValue();
23545         
23546         if(this.groupId){
23547             Roo.bootstrap.CheckBox.register(this);
23548         }
23549     },
23550     
23551     onClick : function(e)
23552     {   
23553         if(this.fireEvent('click', this, e) !== false){
23554             this.setChecked(!this.checked);
23555         }
23556         
23557     },
23558     
23559     setChecked : function(state,suppressEvent)
23560     {
23561         this.startValue = this.getValue();
23562
23563         if(this.inputType == 'radio'){
23564             
23565             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23566                 e.dom.checked = false;
23567             });
23568             
23569             this.inputEl().dom.checked = true;
23570             
23571             this.inputEl().dom.value = this.inputValue;
23572             
23573             if(suppressEvent !== true){
23574                 this.fireEvent('check', this, true);
23575             }
23576             
23577             this.validate();
23578             
23579             return;
23580         }
23581         
23582         this.checked = state;
23583         
23584         this.inputEl().dom.checked = state;
23585         
23586         
23587         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23588         
23589         if(suppressEvent !== true){
23590             this.fireEvent('check', this, state);
23591         }
23592         
23593         this.validate();
23594     },
23595     
23596     getValue : function()
23597     {
23598         if(this.inputType == 'radio'){
23599             return this.getGroupValue();
23600         }
23601         
23602         return this.hiddenEl().dom.value;
23603         
23604     },
23605     
23606     getGroupValue : function()
23607     {
23608         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23609             return '';
23610         }
23611         
23612         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23613     },
23614     
23615     setValue : function(v,suppressEvent)
23616     {
23617         if(this.inputType == 'radio'){
23618             this.setGroupValue(v, suppressEvent);
23619             return;
23620         }
23621         
23622         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23623         
23624         this.validate();
23625     },
23626     
23627     setGroupValue : function(v, suppressEvent)
23628     {
23629         this.startValue = this.getValue();
23630         
23631         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23632             e.dom.checked = false;
23633             
23634             if(e.dom.value == v){
23635                 e.dom.checked = true;
23636             }
23637         });
23638         
23639         if(suppressEvent !== true){
23640             this.fireEvent('check', this, true);
23641         }
23642
23643         this.validate();
23644         
23645         return;
23646     },
23647     
23648     validate : function()
23649     {
23650         if(this.getVisibilityEl().hasClass('hidden')){
23651             return true;
23652         }
23653         
23654         if(
23655                 this.disabled || 
23656                 (this.inputType == 'radio' && this.validateRadio()) ||
23657                 (this.inputType == 'checkbox' && this.validateCheckbox())
23658         ){
23659             this.markValid();
23660             return true;
23661         }
23662         
23663         this.markInvalid();
23664         return false;
23665     },
23666     
23667     validateRadio : function()
23668     {
23669         if(this.getVisibilityEl().hasClass('hidden')){
23670             return true;
23671         }
23672         
23673         if(this.allowBlank){
23674             return true;
23675         }
23676         
23677         var valid = false;
23678         
23679         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23680             if(!e.dom.checked){
23681                 return;
23682             }
23683             
23684             valid = true;
23685             
23686             return false;
23687         });
23688         
23689         return valid;
23690     },
23691     
23692     validateCheckbox : function()
23693     {
23694         if(!this.groupId){
23695             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23696             //return (this.getValue() == this.inputValue) ? true : false;
23697         }
23698         
23699         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23700         
23701         if(!group){
23702             return false;
23703         }
23704         
23705         var r = false;
23706         
23707         for(var i in group){
23708             if(group[i].el.isVisible(true)){
23709                 r = false;
23710                 break;
23711             }
23712             
23713             r = true;
23714         }
23715         
23716         for(var i in group){
23717             if(r){
23718                 break;
23719             }
23720             
23721             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23722         }
23723         
23724         return r;
23725     },
23726     
23727     /**
23728      * Mark this field as valid
23729      */
23730     markValid : function()
23731     {
23732         var _this = this;
23733         
23734         this.fireEvent('valid', this);
23735         
23736         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23737         
23738         if(this.groupId){
23739             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23740         }
23741         
23742         if(label){
23743             label.markValid();
23744         }
23745
23746         if(this.inputType == 'radio'){
23747             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23748                 var fg = e.findParent('.form-group', false, true);
23749                 if (Roo.bootstrap.version == 3) {
23750                     fg.removeClass([_this.invalidClass, _this.validClass]);
23751                     fg.addClass(_this.validClass);
23752                 } else {
23753                     fg.removeClass(['is-valid', 'is-invalid']);
23754                     fg.addClass('is-valid');
23755                 }
23756             });
23757             
23758             return;
23759         }
23760
23761         if(!this.groupId){
23762             var fg = this.el.findParent('.form-group', false, true);
23763             if (Roo.bootstrap.version == 3) {
23764                 fg.removeClass([this.invalidClass, this.validClass]);
23765                 fg.addClass(this.validClass);
23766             } else {
23767                 fg.removeClass(['is-valid', 'is-invalid']);
23768                 fg.addClass('is-valid');
23769             }
23770             return;
23771         }
23772         
23773         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23774         
23775         if(!group){
23776             return;
23777         }
23778         
23779         for(var i in group){
23780             var fg = group[i].el.findParent('.form-group', false, true);
23781             if (Roo.bootstrap.version == 3) {
23782                 fg.removeClass([this.invalidClass, this.validClass]);
23783                 fg.addClass(this.validClass);
23784             } else {
23785                 fg.removeClass(['is-valid', 'is-invalid']);
23786                 fg.addClass('is-valid');
23787             }
23788         }
23789     },
23790     
23791      /**
23792      * Mark this field as invalid
23793      * @param {String} msg The validation message
23794      */
23795     markInvalid : function(msg)
23796     {
23797         if(this.allowBlank){
23798             return;
23799         }
23800         
23801         var _this = this;
23802         
23803         this.fireEvent('invalid', this, msg);
23804         
23805         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23806         
23807         if(this.groupId){
23808             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23809         }
23810         
23811         if(label){
23812             label.markInvalid();
23813         }
23814             
23815         if(this.inputType == 'radio'){
23816             
23817             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23818                 var fg = e.findParent('.form-group', false, true);
23819                 if (Roo.bootstrap.version == 3) {
23820                     fg.removeClass([_this.invalidClass, _this.validClass]);
23821                     fg.addClass(_this.invalidClass);
23822                 } else {
23823                     fg.removeClass(['is-invalid', 'is-valid']);
23824                     fg.addClass('is-invalid');
23825                 }
23826             });
23827             
23828             return;
23829         }
23830         
23831         if(!this.groupId){
23832             var fg = this.el.findParent('.form-group', false, true);
23833             if (Roo.bootstrap.version == 3) {
23834                 fg.removeClass([_this.invalidClass, _this.validClass]);
23835                 fg.addClass(_this.invalidClass);
23836             } else {
23837                 fg.removeClass(['is-invalid', 'is-valid']);
23838                 fg.addClass('is-invalid');
23839             }
23840             return;
23841         }
23842         
23843         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23844         
23845         if(!group){
23846             return;
23847         }
23848         
23849         for(var i in group){
23850             var fg = group[i].el.findParent('.form-group', false, true);
23851             if (Roo.bootstrap.version == 3) {
23852                 fg.removeClass([_this.invalidClass, _this.validClass]);
23853                 fg.addClass(_this.invalidClass);
23854             } else {
23855                 fg.removeClass(['is-invalid', 'is-valid']);
23856                 fg.addClass('is-invalid');
23857             }
23858         }
23859         
23860     },
23861     
23862     clearInvalid : function()
23863     {
23864         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23865         
23866         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23867         
23868         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23869         
23870         if (label && label.iconEl) {
23871             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23872             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23873         }
23874     },
23875     
23876     disable : function()
23877     {
23878         if(this.inputType != 'radio'){
23879             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23880             return;
23881         }
23882         
23883         var _this = this;
23884         
23885         if(this.rendered){
23886             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23887                 _this.getActionEl().addClass(this.disabledClass);
23888                 e.dom.disabled = true;
23889             });
23890         }
23891         
23892         this.disabled = true;
23893         this.fireEvent("disable", this);
23894         return this;
23895     },
23896
23897     enable : function()
23898     {
23899         if(this.inputType != 'radio'){
23900             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23901             return;
23902         }
23903         
23904         var _this = this;
23905         
23906         if(this.rendered){
23907             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23908                 _this.getActionEl().removeClass(this.disabledClass);
23909                 e.dom.disabled = false;
23910             });
23911         }
23912         
23913         this.disabled = false;
23914         this.fireEvent("enable", this);
23915         return this;
23916     },
23917     
23918     setBoxLabel : function(v)
23919     {
23920         this.boxLabel = v;
23921         
23922         if(this.rendered){
23923             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23924         }
23925     }
23926
23927 });
23928
23929 Roo.apply(Roo.bootstrap.CheckBox, {
23930     
23931     groups: {},
23932     
23933      /**
23934     * register a CheckBox Group
23935     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23936     */
23937     register : function(checkbox)
23938     {
23939         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23940             this.groups[checkbox.groupId] = {};
23941         }
23942         
23943         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23944             return;
23945         }
23946         
23947         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23948         
23949     },
23950     /**
23951     * fetch a CheckBox Group based on the group ID
23952     * @param {string} the group ID
23953     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23954     */
23955     get: function(groupId) {
23956         if (typeof(this.groups[groupId]) == 'undefined') {
23957             return false;
23958         }
23959         
23960         return this.groups[groupId] ;
23961     }
23962     
23963     
23964 });
23965 /*
23966  * - LGPL
23967  *
23968  * RadioItem
23969  * 
23970  */
23971
23972 /**
23973  * @class Roo.bootstrap.Radio
23974  * @extends Roo.bootstrap.Component
23975  * Bootstrap Radio class
23976  * @cfg {String} boxLabel - the label associated
23977  * @cfg {String} value - the value of radio
23978  * 
23979  * @constructor
23980  * Create a new Radio
23981  * @param {Object} config The config object
23982  */
23983 Roo.bootstrap.Radio = function(config){
23984     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23985     
23986 };
23987
23988 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23989     
23990     boxLabel : '',
23991     
23992     value : '',
23993     
23994     getAutoCreate : function()
23995     {
23996         var cfg = {
23997             tag : 'div',
23998             cls : 'form-group radio',
23999             cn : [
24000                 {
24001                     tag : 'label',
24002                     cls : 'box-label',
24003                     html : this.boxLabel
24004                 }
24005             ]
24006         };
24007         
24008         return cfg;
24009     },
24010     
24011     initEvents : function() 
24012     {
24013         this.parent().register(this);
24014         
24015         this.el.on('click', this.onClick, this);
24016         
24017     },
24018     
24019     onClick : function(e)
24020     {
24021         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24022             this.setChecked(true);
24023         }
24024     },
24025     
24026     setChecked : function(state, suppressEvent)
24027     {
24028         this.parent().setValue(this.value, suppressEvent);
24029         
24030     },
24031     
24032     setBoxLabel : function(v)
24033     {
24034         this.boxLabel = v;
24035         
24036         if(this.rendered){
24037             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24038         }
24039     }
24040     
24041 });
24042  
24043
24044  /*
24045  * - LGPL
24046  *
24047  * Input
24048  * 
24049  */
24050
24051 /**
24052  * @class Roo.bootstrap.SecurePass
24053  * @extends Roo.bootstrap.Input
24054  * Bootstrap SecurePass class
24055  *
24056  * 
24057  * @constructor
24058  * Create a new SecurePass
24059  * @param {Object} config The config object
24060  */
24061  
24062 Roo.bootstrap.SecurePass = function (config) {
24063     // these go here, so the translation tool can replace them..
24064     this.errors = {
24065         PwdEmpty: "Please type a password, and then retype it to confirm.",
24066         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24067         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24068         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24069         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24070         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24071         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24072         TooWeak: "Your password is Too Weak."
24073     },
24074     this.meterLabel = "Password strength:";
24075     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24076     this.meterClass = [
24077         "roo-password-meter-tooweak", 
24078         "roo-password-meter-weak", 
24079         "roo-password-meter-medium", 
24080         "roo-password-meter-strong", 
24081         "roo-password-meter-grey"
24082     ];
24083     
24084     this.errors = {};
24085     
24086     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24087 }
24088
24089 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24090     /**
24091      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24092      * {
24093      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24094      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24095      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24096      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24097      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24098      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24099      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24100      * })
24101      */
24102     // private
24103     
24104     meterWidth: 300,
24105     errorMsg :'',    
24106     errors: false,
24107     imageRoot: '/',
24108     /**
24109      * @cfg {String/Object} Label for the strength meter (defaults to
24110      * 'Password strength:')
24111      */
24112     // private
24113     meterLabel: '',
24114     /**
24115      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24116      * ['Weak', 'Medium', 'Strong'])
24117      */
24118     // private    
24119     pwdStrengths: false,    
24120     // private
24121     strength: 0,
24122     // private
24123     _lastPwd: null,
24124     // private
24125     kCapitalLetter: 0,
24126     kSmallLetter: 1,
24127     kDigit: 2,
24128     kPunctuation: 3,
24129     
24130     insecure: false,
24131     // private
24132     initEvents: function ()
24133     {
24134         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24135
24136         if (this.el.is('input[type=password]') && Roo.isSafari) {
24137             this.el.on('keydown', this.SafariOnKeyDown, this);
24138         }
24139
24140         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24141     },
24142     // private
24143     onRender: function (ct, position)
24144     {
24145         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24146         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24147         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24148
24149         this.trigger.createChild({
24150                    cn: [
24151                     {
24152                     //id: 'PwdMeter',
24153                     tag: 'div',
24154                     cls: 'roo-password-meter-grey col-xs-12',
24155                     style: {
24156                         //width: 0,
24157                         //width: this.meterWidth + 'px'                                                
24158                         }
24159                     },
24160                     {                            
24161                          cls: 'roo-password-meter-text'                          
24162                     }
24163                 ]            
24164         });
24165
24166          
24167         if (this.hideTrigger) {
24168             this.trigger.setDisplayed(false);
24169         }
24170         this.setSize(this.width || '', this.height || '');
24171     },
24172     // private
24173     onDestroy: function ()
24174     {
24175         if (this.trigger) {
24176             this.trigger.removeAllListeners();
24177             this.trigger.remove();
24178         }
24179         if (this.wrap) {
24180             this.wrap.remove();
24181         }
24182         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24183     },
24184     // private
24185     checkStrength: function ()
24186     {
24187         var pwd = this.inputEl().getValue();
24188         if (pwd == this._lastPwd) {
24189             return;
24190         }
24191
24192         var strength;
24193         if (this.ClientSideStrongPassword(pwd)) {
24194             strength = 3;
24195         } else if (this.ClientSideMediumPassword(pwd)) {
24196             strength = 2;
24197         } else if (this.ClientSideWeakPassword(pwd)) {
24198             strength = 1;
24199         } else {
24200             strength = 0;
24201         }
24202         
24203         Roo.log('strength1: ' + strength);
24204         
24205         //var pm = this.trigger.child('div/div/div').dom;
24206         var pm = this.trigger.child('div/div');
24207         pm.removeClass(this.meterClass);
24208         pm.addClass(this.meterClass[strength]);
24209                 
24210         
24211         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24212                 
24213         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24214         
24215         this._lastPwd = pwd;
24216     },
24217     reset: function ()
24218     {
24219         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24220         
24221         this._lastPwd = '';
24222         
24223         var pm = this.trigger.child('div/div');
24224         pm.removeClass(this.meterClass);
24225         pm.addClass('roo-password-meter-grey');        
24226         
24227         
24228         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24229         
24230         pt.innerHTML = '';
24231         this.inputEl().dom.type='password';
24232     },
24233     // private
24234     validateValue: function (value)
24235     {
24236         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24237             return false;
24238         }
24239         if (value.length == 0) {
24240             if (this.allowBlank) {
24241                 this.clearInvalid();
24242                 return true;
24243             }
24244
24245             this.markInvalid(this.errors.PwdEmpty);
24246             this.errorMsg = this.errors.PwdEmpty;
24247             return false;
24248         }
24249         
24250         if(this.insecure){
24251             return true;
24252         }
24253         
24254         if (!value.match(/[\x21-\x7e]+/)) {
24255             this.markInvalid(this.errors.PwdBadChar);
24256             this.errorMsg = this.errors.PwdBadChar;
24257             return false;
24258         }
24259         if (value.length < 6) {
24260             this.markInvalid(this.errors.PwdShort);
24261             this.errorMsg = this.errors.PwdShort;
24262             return false;
24263         }
24264         if (value.length > 16) {
24265             this.markInvalid(this.errors.PwdLong);
24266             this.errorMsg = this.errors.PwdLong;
24267             return false;
24268         }
24269         var strength;
24270         if (this.ClientSideStrongPassword(value)) {
24271             strength = 3;
24272         } else if (this.ClientSideMediumPassword(value)) {
24273             strength = 2;
24274         } else if (this.ClientSideWeakPassword(value)) {
24275             strength = 1;
24276         } else {
24277             strength = 0;
24278         }
24279
24280         
24281         if (strength < 2) {
24282             //this.markInvalid(this.errors.TooWeak);
24283             this.errorMsg = this.errors.TooWeak;
24284             //return false;
24285         }
24286         
24287         
24288         console.log('strength2: ' + strength);
24289         
24290         //var pm = this.trigger.child('div/div/div').dom;
24291         
24292         var pm = this.trigger.child('div/div');
24293         pm.removeClass(this.meterClass);
24294         pm.addClass(this.meterClass[strength]);
24295                 
24296         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24297                 
24298         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24299         
24300         this.errorMsg = ''; 
24301         return true;
24302     },
24303     // private
24304     CharacterSetChecks: function (type)
24305     {
24306         this.type = type;
24307         this.fResult = false;
24308     },
24309     // private
24310     isctype: function (character, type)
24311     {
24312         switch (type) {  
24313             case this.kCapitalLetter:
24314                 if (character >= 'A' && character <= 'Z') {
24315                     return true;
24316                 }
24317                 break;
24318             
24319             case this.kSmallLetter:
24320                 if (character >= 'a' && character <= 'z') {
24321                     return true;
24322                 }
24323                 break;
24324             
24325             case this.kDigit:
24326                 if (character >= '0' && character <= '9') {
24327                     return true;
24328                 }
24329                 break;
24330             
24331             case this.kPunctuation:
24332                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24333                     return true;
24334                 }
24335                 break;
24336             
24337             default:
24338                 return false;
24339         }
24340
24341     },
24342     // private
24343     IsLongEnough: function (pwd, size)
24344     {
24345         return !(pwd == null || isNaN(size) || pwd.length < size);
24346     },
24347     // private
24348     SpansEnoughCharacterSets: function (word, nb)
24349     {
24350         if (!this.IsLongEnough(word, nb))
24351         {
24352             return false;
24353         }
24354
24355         var characterSetChecks = new Array(
24356             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24357             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24358         );
24359         
24360         for (var index = 0; index < word.length; ++index) {
24361             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24363                     characterSetChecks[nCharSet].fResult = true;
24364                     break;
24365                 }
24366             }
24367         }
24368
24369         var nCharSets = 0;
24370         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24371             if (characterSetChecks[nCharSet].fResult) {
24372                 ++nCharSets;
24373             }
24374         }
24375
24376         if (nCharSets < nb) {
24377             return false;
24378         }
24379         return true;
24380     },
24381     // private
24382     ClientSideStrongPassword: function (pwd)
24383     {
24384         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24385     },
24386     // private
24387     ClientSideMediumPassword: function (pwd)
24388     {
24389         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24390     },
24391     // private
24392     ClientSideWeakPassword: function (pwd)
24393     {
24394         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24395     }
24396           
24397 })//<script type="text/javascript">
24398
24399 /*
24400  * Based  Ext JS Library 1.1.1
24401  * Copyright(c) 2006-2007, Ext JS, LLC.
24402  * LGPL
24403  *
24404  */
24405  
24406 /**
24407  * @class Roo.HtmlEditorCore
24408  * @extends Roo.Component
24409  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24410  *
24411  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24412  */
24413
24414 Roo.HtmlEditorCore = function(config){
24415     
24416     
24417     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24418     
24419     
24420     this.addEvents({
24421         /**
24422          * @event initialize
24423          * Fires when the editor is fully initialized (including the iframe)
24424          * @param {Roo.HtmlEditorCore} this
24425          */
24426         initialize: true,
24427         /**
24428          * @event activate
24429          * Fires when the editor is first receives the focus. Any insertion must wait
24430          * until after this event.
24431          * @param {Roo.HtmlEditorCore} this
24432          */
24433         activate: true,
24434          /**
24435          * @event beforesync
24436          * Fires before the textarea is updated with content from the editor iframe. Return false
24437          * to cancel the sync.
24438          * @param {Roo.HtmlEditorCore} this
24439          * @param {String} html
24440          */
24441         beforesync: true,
24442          /**
24443          * @event beforepush
24444          * Fires before the iframe editor is updated with content from the textarea. Return false
24445          * to cancel the push.
24446          * @param {Roo.HtmlEditorCore} this
24447          * @param {String} html
24448          */
24449         beforepush: true,
24450          /**
24451          * @event sync
24452          * Fires when the textarea is updated with content from the editor iframe.
24453          * @param {Roo.HtmlEditorCore} this
24454          * @param {String} html
24455          */
24456         sync: true,
24457          /**
24458          * @event push
24459          * Fires when the iframe editor is updated with content from the textarea.
24460          * @param {Roo.HtmlEditorCore} this
24461          * @param {String} html
24462          */
24463         push: true,
24464         
24465         /**
24466          * @event editorevent
24467          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24468          * @param {Roo.HtmlEditorCore} this
24469          */
24470         editorevent: true
24471         
24472     });
24473     
24474     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24475     
24476     // defaults : white / black...
24477     this.applyBlacklists();
24478     
24479     
24480     
24481 };
24482
24483
24484 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24485
24486
24487      /**
24488      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24489      */
24490     
24491     owner : false,
24492     
24493      /**
24494      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24495      *                        Roo.resizable.
24496      */
24497     resizable : false,
24498      /**
24499      * @cfg {Number} height (in pixels)
24500      */   
24501     height: 300,
24502    /**
24503      * @cfg {Number} width (in pixels)
24504      */   
24505     width: 500,
24506     
24507     /**
24508      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24509      * 
24510      */
24511     stylesheets: false,
24512     
24513     // id of frame..
24514     frameId: false,
24515     
24516     // private properties
24517     validationEvent : false,
24518     deferHeight: true,
24519     initialized : false,
24520     activated : false,
24521     sourceEditMode : false,
24522     onFocus : Roo.emptyFn,
24523     iframePad:3,
24524     hideMode:'offsets',
24525     
24526     clearUp: true,
24527     
24528     // blacklist + whitelisted elements..
24529     black: false,
24530     white: false,
24531      
24532     bodyCls : '',
24533
24534     /**
24535      * Protected method that will not generally be called directly. It
24536      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24537      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24538      */
24539     getDocMarkup : function(){
24540         // body styles..
24541         var st = '';
24542         
24543         // inherit styels from page...?? 
24544         if (this.stylesheets === false) {
24545             
24546             Roo.get(document.head).select('style').each(function(node) {
24547                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24548             });
24549             
24550             Roo.get(document.head).select('link').each(function(node) { 
24551                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24552             });
24553             
24554         } else if (!this.stylesheets.length) {
24555                 // simple..
24556                 st = '<style type="text/css">' +
24557                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24558                    '</style>';
24559         } else {
24560             for (var i in this.stylesheets) { 
24561                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24562             }
24563             
24564         }
24565         
24566         st +=  '<style type="text/css">' +
24567             'IMG { cursor: pointer } ' +
24568         '</style>';
24569
24570         var cls = 'roo-htmleditor-body';
24571         
24572         if(this.bodyCls.length){
24573             cls += ' ' + this.bodyCls;
24574         }
24575         
24576         return '<html><head>' + st  +
24577             //<style type="text/css">' +
24578             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24579             //'</style>' +
24580             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24581     },
24582
24583     // private
24584     onRender : function(ct, position)
24585     {
24586         var _t = this;
24587         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24588         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24589         
24590         
24591         this.el.dom.style.border = '0 none';
24592         this.el.dom.setAttribute('tabIndex', -1);
24593         this.el.addClass('x-hidden hide');
24594         
24595         
24596         
24597         if(Roo.isIE){ // fix IE 1px bogus margin
24598             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24599         }
24600        
24601         
24602         this.frameId = Roo.id();
24603         
24604          
24605         
24606         var iframe = this.owner.wrap.createChild({
24607             tag: 'iframe',
24608             cls: 'form-control', // bootstrap..
24609             id: this.frameId,
24610             name: this.frameId,
24611             frameBorder : 'no',
24612             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24613         }, this.el
24614         );
24615         
24616         
24617         this.iframe = iframe.dom;
24618
24619          this.assignDocWin();
24620         
24621         this.doc.designMode = 'on';
24622        
24623         this.doc.open();
24624         this.doc.write(this.getDocMarkup());
24625         this.doc.close();
24626
24627         
24628         var task = { // must defer to wait for browser to be ready
24629             run : function(){
24630                 //console.log("run task?" + this.doc.readyState);
24631                 this.assignDocWin();
24632                 if(this.doc.body || this.doc.readyState == 'complete'){
24633                     try {
24634                         this.doc.designMode="on";
24635                     } catch (e) {
24636                         return;
24637                     }
24638                     Roo.TaskMgr.stop(task);
24639                     this.initEditor.defer(10, this);
24640                 }
24641             },
24642             interval : 10,
24643             duration: 10000,
24644             scope: this
24645         };
24646         Roo.TaskMgr.start(task);
24647
24648     },
24649
24650     // private
24651     onResize : function(w, h)
24652     {
24653          Roo.log('resize: ' +w + ',' + h );
24654         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24655         if(!this.iframe){
24656             return;
24657         }
24658         if(typeof w == 'number'){
24659             
24660             this.iframe.style.width = w + 'px';
24661         }
24662         if(typeof h == 'number'){
24663             
24664             this.iframe.style.height = h + 'px';
24665             if(this.doc){
24666                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24667             }
24668         }
24669         
24670     },
24671
24672     /**
24673      * Toggles the editor between standard and source edit mode.
24674      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24675      */
24676     toggleSourceEdit : function(sourceEditMode){
24677         
24678         this.sourceEditMode = sourceEditMode === true;
24679         
24680         if(this.sourceEditMode){
24681  
24682             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24683             
24684         }else{
24685             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24686             //this.iframe.className = '';
24687             this.deferFocus();
24688         }
24689         //this.setSize(this.owner.wrap.getSize());
24690         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24691     },
24692
24693     
24694   
24695
24696     /**
24697      * Protected method that will not generally be called directly. If you need/want
24698      * custom HTML cleanup, this is the method you should override.
24699      * @param {String} html The HTML to be cleaned
24700      * return {String} The cleaned HTML
24701      */
24702     cleanHtml : function(html){
24703         html = String(html);
24704         if(html.length > 5){
24705             if(Roo.isSafari){ // strip safari nonsense
24706                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24707             }
24708         }
24709         if(html == '&nbsp;'){
24710             html = '';
24711         }
24712         return html;
24713     },
24714
24715     /**
24716      * HTML Editor -> Textarea
24717      * Protected method that will not generally be called directly. Syncs the contents
24718      * of the editor iframe with the textarea.
24719      */
24720     syncValue : function(){
24721         if(this.initialized){
24722             var bd = (this.doc.body || this.doc.documentElement);
24723             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24724             var html = bd.innerHTML;
24725             if(Roo.isSafari){
24726                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24727                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24728                 if(m && m[1]){
24729                     html = '<div style="'+m[0]+'">' + html + '</div>';
24730                 }
24731             }
24732             html = this.cleanHtml(html);
24733             // fix up the special chars.. normaly like back quotes in word...
24734             // however we do not want to do this with chinese..
24735             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24736                 
24737                 var cc = match.charCodeAt();
24738
24739                 // Get the character value, handling surrogate pairs
24740                 if (match.length == 2) {
24741                     // It's a surrogate pair, calculate the Unicode code point
24742                     var high = match.charCodeAt(0) - 0xD800;
24743                     var low  = match.charCodeAt(1) - 0xDC00;
24744                     cc = (high * 0x400) + low + 0x10000;
24745                 }  else if (
24746                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24747                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24748                     (cc >= 0xf900 && cc < 0xfb00 )
24749                 ) {
24750                         return match;
24751                 }  
24752          
24753                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24754                 return "&#" + cc + ";";
24755                 
24756                 
24757             });
24758             
24759             
24760              
24761             if(this.owner.fireEvent('beforesync', this, html) !== false){
24762                 this.el.dom.value = html;
24763                 this.owner.fireEvent('sync', this, html);
24764             }
24765         }
24766     },
24767
24768     /**
24769      * Protected method that will not generally be called directly. Pushes the value of the textarea
24770      * into the iframe editor.
24771      */
24772     pushValue : function(){
24773         if(this.initialized){
24774             var v = this.el.dom.value.trim();
24775             
24776 //            if(v.length < 1){
24777 //                v = '&#160;';
24778 //            }
24779             
24780             if(this.owner.fireEvent('beforepush', this, v) !== false){
24781                 var d = (this.doc.body || this.doc.documentElement);
24782                 d.innerHTML = v;
24783                 this.cleanUpPaste();
24784                 this.el.dom.value = d.innerHTML;
24785                 this.owner.fireEvent('push', this, v);
24786             }
24787         }
24788     },
24789
24790     // private
24791     deferFocus : function(){
24792         this.focus.defer(10, this);
24793     },
24794
24795     // doc'ed in Field
24796     focus : function(){
24797         if(this.win && !this.sourceEditMode){
24798             this.win.focus();
24799         }else{
24800             this.el.focus();
24801         }
24802     },
24803     
24804     assignDocWin: function()
24805     {
24806         var iframe = this.iframe;
24807         
24808          if(Roo.isIE){
24809             this.doc = iframe.contentWindow.document;
24810             this.win = iframe.contentWindow;
24811         } else {
24812 //            if (!Roo.get(this.frameId)) {
24813 //                return;
24814 //            }
24815 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24816 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24817             
24818             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24819                 return;
24820             }
24821             
24822             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24823             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24824         }
24825     },
24826     
24827     // private
24828     initEditor : function(){
24829         //console.log("INIT EDITOR");
24830         this.assignDocWin();
24831         
24832         
24833         
24834         this.doc.designMode="on";
24835         this.doc.open();
24836         this.doc.write(this.getDocMarkup());
24837         this.doc.close();
24838         
24839         var dbody = (this.doc.body || this.doc.documentElement);
24840         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24841         // this copies styles from the containing element into thsi one..
24842         // not sure why we need all of this..
24843         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24844         
24845         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24846         //ss['background-attachment'] = 'fixed'; // w3c
24847         dbody.bgProperties = 'fixed'; // ie
24848         //Roo.DomHelper.applyStyles(dbody, ss);
24849         Roo.EventManager.on(this.doc, {
24850             //'mousedown': this.onEditorEvent,
24851             'mouseup': this.onEditorEvent,
24852             'dblclick': this.onEditorEvent,
24853             'click': this.onEditorEvent,
24854             'keyup': this.onEditorEvent,
24855             buffer:100,
24856             scope: this
24857         });
24858         if(Roo.isGecko){
24859             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24860         }
24861         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24862             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24863         }
24864         this.initialized = true;
24865
24866         this.owner.fireEvent('initialize', this);
24867         this.pushValue();
24868     },
24869
24870     // private
24871     onDestroy : function(){
24872         
24873         
24874         
24875         if(this.rendered){
24876             
24877             //for (var i =0; i < this.toolbars.length;i++) {
24878             //    // fixme - ask toolbars for heights?
24879             //    this.toolbars[i].onDestroy();
24880            // }
24881             
24882             //this.wrap.dom.innerHTML = '';
24883             //this.wrap.remove();
24884         }
24885     },
24886
24887     // private
24888     onFirstFocus : function(){
24889         
24890         this.assignDocWin();
24891         
24892         
24893         this.activated = true;
24894          
24895     
24896         if(Roo.isGecko){ // prevent silly gecko errors
24897             this.win.focus();
24898             var s = this.win.getSelection();
24899             if(!s.focusNode || s.focusNode.nodeType != 3){
24900                 var r = s.getRangeAt(0);
24901                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24902                 r.collapse(true);
24903                 this.deferFocus();
24904             }
24905             try{
24906                 this.execCmd('useCSS', true);
24907                 this.execCmd('styleWithCSS', false);
24908             }catch(e){}
24909         }
24910         this.owner.fireEvent('activate', this);
24911     },
24912
24913     // private
24914     adjustFont: function(btn){
24915         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24916         //if(Roo.isSafari){ // safari
24917         //    adjust *= 2;
24918        // }
24919         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24920         if(Roo.isSafari){ // safari
24921             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24922             v =  (v < 10) ? 10 : v;
24923             v =  (v > 48) ? 48 : v;
24924             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24925             
24926         }
24927         
24928         
24929         v = Math.max(1, v+adjust);
24930         
24931         this.execCmd('FontSize', v  );
24932     },
24933
24934     onEditorEvent : function(e)
24935     {
24936         this.owner.fireEvent('editorevent', this, e);
24937       //  this.updateToolbar();
24938         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24939     },
24940
24941     insertTag : function(tg)
24942     {
24943         // could be a bit smarter... -> wrap the current selected tRoo..
24944         if (tg.toLowerCase() == 'span' ||
24945             tg.toLowerCase() == 'code' ||
24946             tg.toLowerCase() == 'sup' ||
24947             tg.toLowerCase() == 'sub' 
24948             ) {
24949             
24950             range = this.createRange(this.getSelection());
24951             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24952             wrappingNode.appendChild(range.extractContents());
24953             range.insertNode(wrappingNode);
24954
24955             return;
24956             
24957             
24958             
24959         }
24960         this.execCmd("formatblock",   tg);
24961         
24962     },
24963     
24964     insertText : function(txt)
24965     {
24966         
24967         
24968         var range = this.createRange();
24969         range.deleteContents();
24970                //alert(Sender.getAttribute('label'));
24971                
24972         range.insertNode(this.doc.createTextNode(txt));
24973     } ,
24974     
24975      
24976
24977     /**
24978      * Executes a Midas editor command on the editor document and performs necessary focus and
24979      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24980      * @param {String} cmd The Midas command
24981      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24982      */
24983     relayCmd : function(cmd, value){
24984         this.win.focus();
24985         this.execCmd(cmd, value);
24986         this.owner.fireEvent('editorevent', this);
24987         //this.updateToolbar();
24988         this.owner.deferFocus();
24989     },
24990
24991     /**
24992      * Executes a Midas editor command directly on the editor document.
24993      * For visual commands, you should use {@link #relayCmd} instead.
24994      * <b>This should only be called after the editor is initialized.</b>
24995      * @param {String} cmd The Midas command
24996      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24997      */
24998     execCmd : function(cmd, value){
24999         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25000         this.syncValue();
25001     },
25002  
25003  
25004    
25005     /**
25006      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25007      * to insert tRoo.
25008      * @param {String} text | dom node.. 
25009      */
25010     insertAtCursor : function(text)
25011     {
25012         
25013         if(!this.activated){
25014             return;
25015         }
25016         /*
25017         if(Roo.isIE){
25018             this.win.focus();
25019             var r = this.doc.selection.createRange();
25020             if(r){
25021                 r.collapse(true);
25022                 r.pasteHTML(text);
25023                 this.syncValue();
25024                 this.deferFocus();
25025             
25026             }
25027             return;
25028         }
25029         */
25030         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25031             this.win.focus();
25032             
25033             
25034             // from jquery ui (MIT licenced)
25035             var range, node;
25036             var win = this.win;
25037             
25038             if (win.getSelection && win.getSelection().getRangeAt) {
25039                 range = win.getSelection().getRangeAt(0);
25040                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25041                 range.insertNode(node);
25042             } else if (win.document.selection && win.document.selection.createRange) {
25043                 // no firefox support
25044                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25045                 win.document.selection.createRange().pasteHTML(txt);
25046             } else {
25047                 // no firefox support
25048                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25049                 this.execCmd('InsertHTML', txt);
25050             } 
25051             
25052             this.syncValue();
25053             
25054             this.deferFocus();
25055         }
25056     },
25057  // private
25058     mozKeyPress : function(e){
25059         if(e.ctrlKey){
25060             var c = e.getCharCode(), cmd;
25061           
25062             if(c > 0){
25063                 c = String.fromCharCode(c).toLowerCase();
25064                 switch(c){
25065                     case 'b':
25066                         cmd = 'bold';
25067                         break;
25068                     case 'i':
25069                         cmd = 'italic';
25070                         break;
25071                     
25072                     case 'u':
25073                         cmd = 'underline';
25074                         break;
25075                     
25076                     case 'v':
25077                         this.cleanUpPaste.defer(100, this);
25078                         return;
25079                         
25080                 }
25081                 if(cmd){
25082                     this.win.focus();
25083                     this.execCmd(cmd);
25084                     this.deferFocus();
25085                     e.preventDefault();
25086                 }
25087                 
25088             }
25089         }
25090     },
25091
25092     // private
25093     fixKeys : function(){ // load time branching for fastest keydown performance
25094         if(Roo.isIE){
25095             return function(e){
25096                 var k = e.getKey(), r;
25097                 if(k == e.TAB){
25098                     e.stopEvent();
25099                     r = this.doc.selection.createRange();
25100                     if(r){
25101                         r.collapse(true);
25102                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25103                         this.deferFocus();
25104                     }
25105                     return;
25106                 }
25107                 
25108                 if(k == e.ENTER){
25109                     r = this.doc.selection.createRange();
25110                     if(r){
25111                         var target = r.parentElement();
25112                         if(!target || target.tagName.toLowerCase() != 'li'){
25113                             e.stopEvent();
25114                             r.pasteHTML('<br />');
25115                             r.collapse(false);
25116                             r.select();
25117                         }
25118                     }
25119                 }
25120                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25121                     this.cleanUpPaste.defer(100, this);
25122                     return;
25123                 }
25124                 
25125                 
25126             };
25127         }else if(Roo.isOpera){
25128             return function(e){
25129                 var k = e.getKey();
25130                 if(k == e.TAB){
25131                     e.stopEvent();
25132                     this.win.focus();
25133                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25134                     this.deferFocus();
25135                 }
25136                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25137                     this.cleanUpPaste.defer(100, this);
25138                     return;
25139                 }
25140                 
25141             };
25142         }else if(Roo.isSafari){
25143             return function(e){
25144                 var k = e.getKey();
25145                 
25146                 if(k == e.TAB){
25147                     e.stopEvent();
25148                     this.execCmd('InsertText','\t');
25149                     this.deferFocus();
25150                     return;
25151                 }
25152                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25153                     this.cleanUpPaste.defer(100, this);
25154                     return;
25155                 }
25156                 
25157              };
25158         }
25159     }(),
25160     
25161     getAllAncestors: function()
25162     {
25163         var p = this.getSelectedNode();
25164         var a = [];
25165         if (!p) {
25166             a.push(p); // push blank onto stack..
25167             p = this.getParentElement();
25168         }
25169         
25170         
25171         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25172             a.push(p);
25173             p = p.parentNode;
25174         }
25175         a.push(this.doc.body);
25176         return a;
25177     },
25178     lastSel : false,
25179     lastSelNode : false,
25180     
25181     
25182     getSelection : function() 
25183     {
25184         this.assignDocWin();
25185         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25186     },
25187     
25188     getSelectedNode: function() 
25189     {
25190         // this may only work on Gecko!!!
25191         
25192         // should we cache this!!!!
25193         
25194         
25195         
25196          
25197         var range = this.createRange(this.getSelection()).cloneRange();
25198         
25199         if (Roo.isIE) {
25200             var parent = range.parentElement();
25201             while (true) {
25202                 var testRange = range.duplicate();
25203                 testRange.moveToElementText(parent);
25204                 if (testRange.inRange(range)) {
25205                     break;
25206                 }
25207                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25208                     break;
25209                 }
25210                 parent = parent.parentElement;
25211             }
25212             return parent;
25213         }
25214         
25215         // is ancestor a text element.
25216         var ac =  range.commonAncestorContainer;
25217         if (ac.nodeType == 3) {
25218             ac = ac.parentNode;
25219         }
25220         
25221         var ar = ac.childNodes;
25222          
25223         var nodes = [];
25224         var other_nodes = [];
25225         var has_other_nodes = false;
25226         for (var i=0;i<ar.length;i++) {
25227             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25228                 continue;
25229             }
25230             // fullly contained node.
25231             
25232             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25233                 nodes.push(ar[i]);
25234                 continue;
25235             }
25236             
25237             // probably selected..
25238             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25239                 other_nodes.push(ar[i]);
25240                 continue;
25241             }
25242             // outer..
25243             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25244                 continue;
25245             }
25246             
25247             
25248             has_other_nodes = true;
25249         }
25250         if (!nodes.length && other_nodes.length) {
25251             nodes= other_nodes;
25252         }
25253         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25254             return false;
25255         }
25256         
25257         return nodes[0];
25258     },
25259     createRange: function(sel)
25260     {
25261         // this has strange effects when using with 
25262         // top toolbar - not sure if it's a great idea.
25263         //this.editor.contentWindow.focus();
25264         if (typeof sel != "undefined") {
25265             try {
25266                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25267             } catch(e) {
25268                 return this.doc.createRange();
25269             }
25270         } else {
25271             return this.doc.createRange();
25272         }
25273     },
25274     getParentElement: function()
25275     {
25276         
25277         this.assignDocWin();
25278         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25279         
25280         var range = this.createRange(sel);
25281          
25282         try {
25283             var p = range.commonAncestorContainer;
25284             while (p.nodeType == 3) { // text node
25285                 p = p.parentNode;
25286             }
25287             return p;
25288         } catch (e) {
25289             return null;
25290         }
25291     
25292     },
25293     /***
25294      *
25295      * Range intersection.. the hard stuff...
25296      *  '-1' = before
25297      *  '0' = hits..
25298      *  '1' = after.
25299      *         [ -- selected range --- ]
25300      *   [fail]                        [fail]
25301      *
25302      *    basically..
25303      *      if end is before start or  hits it. fail.
25304      *      if start is after end or hits it fail.
25305      *
25306      *   if either hits (but other is outside. - then it's not 
25307      *   
25308      *    
25309      **/
25310     
25311     
25312     // @see http://www.thismuchiknow.co.uk/?p=64.
25313     rangeIntersectsNode : function(range, node)
25314     {
25315         var nodeRange = node.ownerDocument.createRange();
25316         try {
25317             nodeRange.selectNode(node);
25318         } catch (e) {
25319             nodeRange.selectNodeContents(node);
25320         }
25321     
25322         var rangeStartRange = range.cloneRange();
25323         rangeStartRange.collapse(true);
25324     
25325         var rangeEndRange = range.cloneRange();
25326         rangeEndRange.collapse(false);
25327     
25328         var nodeStartRange = nodeRange.cloneRange();
25329         nodeStartRange.collapse(true);
25330     
25331         var nodeEndRange = nodeRange.cloneRange();
25332         nodeEndRange.collapse(false);
25333     
25334         return rangeStartRange.compareBoundaryPoints(
25335                  Range.START_TO_START, nodeEndRange) == -1 &&
25336                rangeEndRange.compareBoundaryPoints(
25337                  Range.START_TO_START, nodeStartRange) == 1;
25338         
25339          
25340     },
25341     rangeCompareNode : function(range, node)
25342     {
25343         var nodeRange = node.ownerDocument.createRange();
25344         try {
25345             nodeRange.selectNode(node);
25346         } catch (e) {
25347             nodeRange.selectNodeContents(node);
25348         }
25349         
25350         
25351         range.collapse(true);
25352     
25353         nodeRange.collapse(true);
25354      
25355         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25356         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25357          
25358         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25359         
25360         var nodeIsBefore   =  ss == 1;
25361         var nodeIsAfter    = ee == -1;
25362         
25363         if (nodeIsBefore && nodeIsAfter) {
25364             return 0; // outer
25365         }
25366         if (!nodeIsBefore && nodeIsAfter) {
25367             return 1; //right trailed.
25368         }
25369         
25370         if (nodeIsBefore && !nodeIsAfter) {
25371             return 2;  // left trailed.
25372         }
25373         // fully contined.
25374         return 3;
25375     },
25376
25377     // private? - in a new class?
25378     cleanUpPaste :  function()
25379     {
25380         // cleans up the whole document..
25381         Roo.log('cleanuppaste');
25382         
25383         this.cleanUpChildren(this.doc.body);
25384         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25385         if (clean != this.doc.body.innerHTML) {
25386             this.doc.body.innerHTML = clean;
25387         }
25388         
25389     },
25390     
25391     cleanWordChars : function(input) {// change the chars to hex code
25392         var he = Roo.HtmlEditorCore;
25393         
25394         var output = input;
25395         Roo.each(he.swapCodes, function(sw) { 
25396             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25397             
25398             output = output.replace(swapper, sw[1]);
25399         });
25400         
25401         return output;
25402     },
25403     
25404     
25405     cleanUpChildren : function (n)
25406     {
25407         if (!n.childNodes.length) {
25408             return;
25409         }
25410         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25411            this.cleanUpChild(n.childNodes[i]);
25412         }
25413     },
25414     
25415     
25416         
25417     
25418     cleanUpChild : function (node)
25419     {
25420         var ed = this;
25421         //console.log(node);
25422         if (node.nodeName == "#text") {
25423             // clean up silly Windows -- stuff?
25424             return; 
25425         }
25426         if (node.nodeName == "#comment") {
25427             node.parentNode.removeChild(node);
25428             // clean up silly Windows -- stuff?
25429             return; 
25430         }
25431         var lcname = node.tagName.toLowerCase();
25432         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25433         // whitelist of tags..
25434         
25435         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25436             // remove node.
25437             node.parentNode.removeChild(node);
25438             return;
25439             
25440         }
25441         
25442         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25443         
25444         // spans with no attributes - just remove them..
25445         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25446             remove_keep_children = true;
25447         }
25448         
25449         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25450         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25451         
25452         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25453         //    remove_keep_children = true;
25454         //}
25455         
25456         if (remove_keep_children) {
25457             this.cleanUpChildren(node);
25458             // inserts everything just before this node...
25459             while (node.childNodes.length) {
25460                 var cn = node.childNodes[0];
25461                 node.removeChild(cn);
25462                 node.parentNode.insertBefore(cn, node);
25463             }
25464             node.parentNode.removeChild(node);
25465             return;
25466         }
25467         
25468         if (!node.attributes || !node.attributes.length) {
25469             
25470           
25471             
25472             
25473             this.cleanUpChildren(node);
25474             return;
25475         }
25476         
25477         function cleanAttr(n,v)
25478         {
25479             
25480             if (v.match(/^\./) || v.match(/^\//)) {
25481                 return;
25482             }
25483             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25484                 return;
25485             }
25486             if (v.match(/^#/)) {
25487                 return;
25488             }
25489             if (v.match(/^\{/)) { // allow template editing.
25490                 return;
25491             }
25492 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25493             node.removeAttribute(n);
25494             
25495         }
25496         
25497         var cwhite = this.cwhite;
25498         var cblack = this.cblack;
25499             
25500         function cleanStyle(n,v)
25501         {
25502             if (v.match(/expression/)) { //XSS?? should we even bother..
25503                 node.removeAttribute(n);
25504                 return;
25505             }
25506             
25507             var parts = v.split(/;/);
25508             var clean = [];
25509             
25510             Roo.each(parts, function(p) {
25511                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25512                 if (!p.length) {
25513                     return true;
25514                 }
25515                 var l = p.split(':').shift().replace(/\s+/g,'');
25516                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25517                 
25518                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25519 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25520                     //node.removeAttribute(n);
25521                     return true;
25522                 }
25523                 //Roo.log()
25524                 // only allow 'c whitelisted system attributes'
25525                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25526 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25527                     //node.removeAttribute(n);
25528                     return true;
25529                 }
25530                 
25531                 
25532                  
25533                 
25534                 clean.push(p);
25535                 return true;
25536             });
25537             if (clean.length) { 
25538                 node.setAttribute(n, clean.join(';'));
25539             } else {
25540                 node.removeAttribute(n);
25541             }
25542             
25543         }
25544         
25545         
25546         for (var i = node.attributes.length-1; i > -1 ; i--) {
25547             var a = node.attributes[i];
25548             //console.log(a);
25549             
25550             if (a.name.toLowerCase().substr(0,2)=='on')  {
25551                 node.removeAttribute(a.name);
25552                 continue;
25553             }
25554             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25555                 node.removeAttribute(a.name);
25556                 continue;
25557             }
25558             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25559                 cleanAttr(a.name,a.value); // fixme..
25560                 continue;
25561             }
25562             if (a.name == 'style') {
25563                 cleanStyle(a.name,a.value);
25564                 continue;
25565             }
25566             /// clean up MS crap..
25567             // tecnically this should be a list of valid class'es..
25568             
25569             
25570             if (a.name == 'class') {
25571                 if (a.value.match(/^Mso/)) {
25572                     node.removeAttribute('class');
25573                 }
25574                 
25575                 if (a.value.match(/^body$/)) {
25576                     node.removeAttribute('class');
25577                 }
25578                 continue;
25579             }
25580             
25581             // style cleanup!?
25582             // class cleanup?
25583             
25584         }
25585         
25586         
25587         this.cleanUpChildren(node);
25588         
25589         
25590     },
25591     
25592     /**
25593      * Clean up MS wordisms...
25594      */
25595     cleanWord : function(node)
25596     {
25597         if (!node) {
25598             this.cleanWord(this.doc.body);
25599             return;
25600         }
25601         
25602         if(
25603                 node.nodeName == 'SPAN' &&
25604                 !node.hasAttributes() &&
25605                 node.childNodes.length == 1 &&
25606                 node.firstChild.nodeName == "#text"  
25607         ) {
25608             var textNode = node.firstChild;
25609             node.removeChild(textNode);
25610             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25611                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25612             }
25613             node.parentNode.insertBefore(textNode, node);
25614             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25615                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25616             }
25617             node.parentNode.removeChild(node);
25618         }
25619         
25620         if (node.nodeName == "#text") {
25621             // clean up silly Windows -- stuff?
25622             return; 
25623         }
25624         if (node.nodeName == "#comment") {
25625             node.parentNode.removeChild(node);
25626             // clean up silly Windows -- stuff?
25627             return; 
25628         }
25629         
25630         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25631             node.parentNode.removeChild(node);
25632             return;
25633         }
25634         //Roo.log(node.tagName);
25635         // remove - but keep children..
25636         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25637             //Roo.log('-- removed');
25638             while (node.childNodes.length) {
25639                 var cn = node.childNodes[0];
25640                 node.removeChild(cn);
25641                 node.parentNode.insertBefore(cn, node);
25642                 // move node to parent - and clean it..
25643                 this.cleanWord(cn);
25644             }
25645             node.parentNode.removeChild(node);
25646             /// no need to iterate chidlren = it's got none..
25647             //this.iterateChildren(node, this.cleanWord);
25648             return;
25649         }
25650         // clean styles
25651         if (node.className.length) {
25652             
25653             var cn = node.className.split(/\W+/);
25654             var cna = [];
25655             Roo.each(cn, function(cls) {
25656                 if (cls.match(/Mso[a-zA-Z]+/)) {
25657                     return;
25658                 }
25659                 cna.push(cls);
25660             });
25661             node.className = cna.length ? cna.join(' ') : '';
25662             if (!cna.length) {
25663                 node.removeAttribute("class");
25664             }
25665         }
25666         
25667         if (node.hasAttribute("lang")) {
25668             node.removeAttribute("lang");
25669         }
25670         
25671         if (node.hasAttribute("style")) {
25672             
25673             var styles = node.getAttribute("style").split(";");
25674             var nstyle = [];
25675             Roo.each(styles, function(s) {
25676                 if (!s.match(/:/)) {
25677                     return;
25678                 }
25679                 var kv = s.split(":");
25680                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25681                     return;
25682                 }
25683                 // what ever is left... we allow.
25684                 nstyle.push(s);
25685             });
25686             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25687             if (!nstyle.length) {
25688                 node.removeAttribute('style');
25689             }
25690         }
25691         this.iterateChildren(node, this.cleanWord);
25692         
25693         
25694         
25695     },
25696     /**
25697      * iterateChildren of a Node, calling fn each time, using this as the scole..
25698      * @param {DomNode} node node to iterate children of.
25699      * @param {Function} fn method of this class to call on each item.
25700      */
25701     iterateChildren : function(node, fn)
25702     {
25703         if (!node.childNodes.length) {
25704                 return;
25705         }
25706         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25707            fn.call(this, node.childNodes[i])
25708         }
25709     },
25710     
25711     
25712     /**
25713      * cleanTableWidths.
25714      *
25715      * Quite often pasting from word etc.. results in tables with column and widths.
25716      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25717      *
25718      */
25719     cleanTableWidths : function(node)
25720     {
25721          
25722          
25723         if (!node) {
25724             this.cleanTableWidths(this.doc.body);
25725             return;
25726         }
25727         
25728         // ignore list...
25729         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25730             return; 
25731         }
25732         Roo.log(node.tagName);
25733         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25734             this.iterateChildren(node, this.cleanTableWidths);
25735             return;
25736         }
25737         if (node.hasAttribute('width')) {
25738             node.removeAttribute('width');
25739         }
25740         
25741          
25742         if (node.hasAttribute("style")) {
25743             // pretty basic...
25744             
25745             var styles = node.getAttribute("style").split(";");
25746             var nstyle = [];
25747             Roo.each(styles, function(s) {
25748                 if (!s.match(/:/)) {
25749                     return;
25750                 }
25751                 var kv = s.split(":");
25752                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25753                     return;
25754                 }
25755                 // what ever is left... we allow.
25756                 nstyle.push(s);
25757             });
25758             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25759             if (!nstyle.length) {
25760                 node.removeAttribute('style');
25761             }
25762         }
25763         
25764         this.iterateChildren(node, this.cleanTableWidths);
25765         
25766         
25767     },
25768     
25769     
25770     
25771     
25772     domToHTML : function(currentElement, depth, nopadtext) {
25773         
25774         depth = depth || 0;
25775         nopadtext = nopadtext || false;
25776     
25777         if (!currentElement) {
25778             return this.domToHTML(this.doc.body);
25779         }
25780         
25781         //Roo.log(currentElement);
25782         var j;
25783         var allText = false;
25784         var nodeName = currentElement.nodeName;
25785         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25786         
25787         if  (nodeName == '#text') {
25788             
25789             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25790         }
25791         
25792         
25793         var ret = '';
25794         if (nodeName != 'BODY') {
25795              
25796             var i = 0;
25797             // Prints the node tagName, such as <A>, <IMG>, etc
25798             if (tagName) {
25799                 var attr = [];
25800                 for(i = 0; i < currentElement.attributes.length;i++) {
25801                     // quoting?
25802                     var aname = currentElement.attributes.item(i).name;
25803                     if (!currentElement.attributes.item(i).value.length) {
25804                         continue;
25805                     }
25806                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25807                 }
25808                 
25809                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25810             } 
25811             else {
25812                 
25813                 // eack
25814             }
25815         } else {
25816             tagName = false;
25817         }
25818         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25819             return ret;
25820         }
25821         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25822             nopadtext = true;
25823         }
25824         
25825         
25826         // Traverse the tree
25827         i = 0;
25828         var currentElementChild = currentElement.childNodes.item(i);
25829         var allText = true;
25830         var innerHTML  = '';
25831         lastnode = '';
25832         while (currentElementChild) {
25833             // Formatting code (indent the tree so it looks nice on the screen)
25834             var nopad = nopadtext;
25835             if (lastnode == 'SPAN') {
25836                 nopad  = true;
25837             }
25838             // text
25839             if  (currentElementChild.nodeName == '#text') {
25840                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25841                 toadd = nopadtext ? toadd : toadd.trim();
25842                 if (!nopad && toadd.length > 80) {
25843                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25844                 }
25845                 innerHTML  += toadd;
25846                 
25847                 i++;
25848                 currentElementChild = currentElement.childNodes.item(i);
25849                 lastNode = '';
25850                 continue;
25851             }
25852             allText = false;
25853             
25854             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25855                 
25856             // Recursively traverse the tree structure of the child node
25857             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25858             lastnode = currentElementChild.nodeName;
25859             i++;
25860             currentElementChild=currentElement.childNodes.item(i);
25861         }
25862         
25863         ret += innerHTML;
25864         
25865         if (!allText) {
25866                 // The remaining code is mostly for formatting the tree
25867             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25868         }
25869         
25870         
25871         if (tagName) {
25872             ret+= "</"+tagName+">";
25873         }
25874         return ret;
25875         
25876     },
25877         
25878     applyBlacklists : function()
25879     {
25880         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25881         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25882         
25883         this.white = [];
25884         this.black = [];
25885         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25886             if (b.indexOf(tag) > -1) {
25887                 return;
25888             }
25889             this.white.push(tag);
25890             
25891         }, this);
25892         
25893         Roo.each(w, function(tag) {
25894             if (b.indexOf(tag) > -1) {
25895                 return;
25896             }
25897             if (this.white.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.white.push(tag);
25901             
25902         }, this);
25903         
25904         
25905         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25906             if (w.indexOf(tag) > -1) {
25907                 return;
25908             }
25909             this.black.push(tag);
25910             
25911         }, this);
25912         
25913         Roo.each(b, function(tag) {
25914             if (w.indexOf(tag) > -1) {
25915                 return;
25916             }
25917             if (this.black.indexOf(tag) > -1) {
25918                 return;
25919             }
25920             this.black.push(tag);
25921             
25922         }, this);
25923         
25924         
25925         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25926         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25927         
25928         this.cwhite = [];
25929         this.cblack = [];
25930         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25931             if (b.indexOf(tag) > -1) {
25932                 return;
25933             }
25934             this.cwhite.push(tag);
25935             
25936         }, this);
25937         
25938         Roo.each(w, function(tag) {
25939             if (b.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             if (this.cwhite.indexOf(tag) > -1) {
25943                 return;
25944             }
25945             this.cwhite.push(tag);
25946             
25947         }, this);
25948         
25949         
25950         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25951             if (w.indexOf(tag) > -1) {
25952                 return;
25953             }
25954             this.cblack.push(tag);
25955             
25956         }, this);
25957         
25958         Roo.each(b, function(tag) {
25959             if (w.indexOf(tag) > -1) {
25960                 return;
25961             }
25962             if (this.cblack.indexOf(tag) > -1) {
25963                 return;
25964             }
25965             this.cblack.push(tag);
25966             
25967         }, this);
25968     },
25969     
25970     setStylesheets : function(stylesheets)
25971     {
25972         if(typeof(stylesheets) == 'string'){
25973             Roo.get(this.iframe.contentDocument.head).createChild({
25974                 tag : 'link',
25975                 rel : 'stylesheet',
25976                 type : 'text/css',
25977                 href : stylesheets
25978             });
25979             
25980             return;
25981         }
25982         var _this = this;
25983      
25984         Roo.each(stylesheets, function(s) {
25985             if(!s.length){
25986                 return;
25987             }
25988             
25989             Roo.get(_this.iframe.contentDocument.head).createChild({
25990                 tag : 'link',
25991                 rel : 'stylesheet',
25992                 type : 'text/css',
25993                 href : s
25994             });
25995         });
25996
25997         
25998     },
25999     
26000     removeStylesheets : function()
26001     {
26002         var _this = this;
26003         
26004         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26005             s.remove();
26006         });
26007     },
26008     
26009     setStyle : function(style)
26010     {
26011         Roo.get(this.iframe.contentDocument.head).createChild({
26012             tag : 'style',
26013             type : 'text/css',
26014             html : style
26015         });
26016
26017         return;
26018     }
26019     
26020     // hide stuff that is not compatible
26021     /**
26022      * @event blur
26023      * @hide
26024      */
26025     /**
26026      * @event change
26027      * @hide
26028      */
26029     /**
26030      * @event focus
26031      * @hide
26032      */
26033     /**
26034      * @event specialkey
26035      * @hide
26036      */
26037     /**
26038      * @cfg {String} fieldClass @hide
26039      */
26040     /**
26041      * @cfg {String} focusClass @hide
26042      */
26043     /**
26044      * @cfg {String} autoCreate @hide
26045      */
26046     /**
26047      * @cfg {String} inputType @hide
26048      */
26049     /**
26050      * @cfg {String} invalidClass @hide
26051      */
26052     /**
26053      * @cfg {String} invalidText @hide
26054      */
26055     /**
26056      * @cfg {String} msgFx @hide
26057      */
26058     /**
26059      * @cfg {String} validateOnBlur @hide
26060      */
26061 });
26062
26063 Roo.HtmlEditorCore.white = [
26064         'area', 'br', 'img', 'input', 'hr', 'wbr',
26065         
26066        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26067        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26068        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26069        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26070        'table',   'ul',         'xmp', 
26071        
26072        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26073       'thead',   'tr', 
26074      
26075       'dir', 'menu', 'ol', 'ul', 'dl',
26076        
26077       'embed',  'object'
26078 ];
26079
26080
26081 Roo.HtmlEditorCore.black = [
26082     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26083         'applet', // 
26084         'base',   'basefont', 'bgsound', 'blink',  'body', 
26085         'frame',  'frameset', 'head',    'html',   'ilayer', 
26086         'iframe', 'layer',  'link',     'meta',    'object',   
26087         'script', 'style' ,'title',  'xml' // clean later..
26088 ];
26089 Roo.HtmlEditorCore.clean = [
26090     'script', 'style', 'title', 'xml'
26091 ];
26092 Roo.HtmlEditorCore.remove = [
26093     'font'
26094 ];
26095 // attributes..
26096
26097 Roo.HtmlEditorCore.ablack = [
26098     'on'
26099 ];
26100     
26101 Roo.HtmlEditorCore.aclean = [ 
26102     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26103 ];
26104
26105 // protocols..
26106 Roo.HtmlEditorCore.pwhite= [
26107         'http',  'https',  'mailto'
26108 ];
26109
26110 // white listed style attributes.
26111 Roo.HtmlEditorCore.cwhite= [
26112       //  'text-align', /// default is to allow most things..
26113       
26114          
26115 //        'font-size'//??
26116 ];
26117
26118 // black listed style attributes.
26119 Roo.HtmlEditorCore.cblack= [
26120       //  'font-size' -- this can be set by the project 
26121 ];
26122
26123
26124 Roo.HtmlEditorCore.swapCodes   =[ 
26125     [    8211, "&#8211;" ], 
26126     [    8212, "&#8212;" ], 
26127     [    8216,  "'" ],  
26128     [    8217, "'" ],  
26129     [    8220, '"' ],  
26130     [    8221, '"' ],  
26131     [    8226, "*" ],  
26132     [    8230, "..." ]
26133 ]; 
26134
26135     /*
26136  * - LGPL
26137  *
26138  * HtmlEditor
26139  * 
26140  */
26141
26142 /**
26143  * @class Roo.bootstrap.HtmlEditor
26144  * @extends Roo.bootstrap.TextArea
26145  * Bootstrap HtmlEditor class
26146
26147  * @constructor
26148  * Create a new HtmlEditor
26149  * @param {Object} config The config object
26150  */
26151
26152 Roo.bootstrap.HtmlEditor = function(config){
26153     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26154     if (!this.toolbars) {
26155         this.toolbars = [];
26156     }
26157     
26158     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26159     this.addEvents({
26160             /**
26161              * @event initialize
26162              * Fires when the editor is fully initialized (including the iframe)
26163              * @param {HtmlEditor} this
26164              */
26165             initialize: true,
26166             /**
26167              * @event activate
26168              * Fires when the editor is first receives the focus. Any insertion must wait
26169              * until after this event.
26170              * @param {HtmlEditor} this
26171              */
26172             activate: true,
26173              /**
26174              * @event beforesync
26175              * Fires before the textarea is updated with content from the editor iframe. Return false
26176              * to cancel the sync.
26177              * @param {HtmlEditor} this
26178              * @param {String} html
26179              */
26180             beforesync: true,
26181              /**
26182              * @event beforepush
26183              * Fires before the iframe editor is updated with content from the textarea. Return false
26184              * to cancel the push.
26185              * @param {HtmlEditor} this
26186              * @param {String} html
26187              */
26188             beforepush: true,
26189              /**
26190              * @event sync
26191              * Fires when the textarea is updated with content from the editor iframe.
26192              * @param {HtmlEditor} this
26193              * @param {String} html
26194              */
26195             sync: true,
26196              /**
26197              * @event push
26198              * Fires when the iframe editor is updated with content from the textarea.
26199              * @param {HtmlEditor} this
26200              * @param {String} html
26201              */
26202             push: true,
26203              /**
26204              * @event editmodechange
26205              * Fires when the editor switches edit modes
26206              * @param {HtmlEditor} this
26207              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26208              */
26209             editmodechange: true,
26210             /**
26211              * @event editorevent
26212              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26213              * @param {HtmlEditor} this
26214              */
26215             editorevent: true,
26216             /**
26217              * @event firstfocus
26218              * Fires when on first focus - needed by toolbars..
26219              * @param {HtmlEditor} this
26220              */
26221             firstfocus: true,
26222             /**
26223              * @event autosave
26224              * Auto save the htmlEditor value as a file into Events
26225              * @param {HtmlEditor} this
26226              */
26227             autosave: true,
26228             /**
26229              * @event savedpreview
26230              * preview the saved version of htmlEditor
26231              * @param {HtmlEditor} this
26232              */
26233             savedpreview: true
26234         });
26235 };
26236
26237
26238 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26239     
26240     
26241       /**
26242      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26243      */
26244     toolbars : false,
26245     
26246      /**
26247     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26248     */
26249     btns : [],
26250    
26251      /**
26252      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26253      *                        Roo.resizable.
26254      */
26255     resizable : false,
26256      /**
26257      * @cfg {Number} height (in pixels)
26258      */   
26259     height: 300,
26260    /**
26261      * @cfg {Number} width (in pixels)
26262      */   
26263     width: false,
26264     
26265     /**
26266      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26267      * 
26268      */
26269     stylesheets: false,
26270     
26271     // id of frame..
26272     frameId: false,
26273     
26274     // private properties
26275     validationEvent : false,
26276     deferHeight: true,
26277     initialized : false,
26278     activated : false,
26279     
26280     onFocus : Roo.emptyFn,
26281     iframePad:3,
26282     hideMode:'offsets',
26283     
26284     tbContainer : false,
26285     
26286     bodyCls : '',
26287     
26288     toolbarContainer :function() {
26289         return this.wrap.select('.x-html-editor-tb',true).first();
26290     },
26291
26292     /**
26293      * Protected method that will not generally be called directly. It
26294      * is called when the editor creates its toolbar. Override this method if you need to
26295      * add custom toolbar buttons.
26296      * @param {HtmlEditor} editor
26297      */
26298     createToolbar : function(){
26299         Roo.log('renewing');
26300         Roo.log("create toolbars");
26301         
26302         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26303         this.toolbars[0].render(this.toolbarContainer());
26304         
26305         return;
26306         
26307 //        if (!editor.toolbars || !editor.toolbars.length) {
26308 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26309 //        }
26310 //        
26311 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26312 //            editor.toolbars[i] = Roo.factory(
26313 //                    typeof(editor.toolbars[i]) == 'string' ?
26314 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26315 //                Roo.bootstrap.HtmlEditor);
26316 //            editor.toolbars[i].init(editor);
26317 //        }
26318     },
26319
26320      
26321     // private
26322     onRender : function(ct, position)
26323     {
26324        // Roo.log("Call onRender: " + this.xtype);
26325         var _t = this;
26326         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26327       
26328         this.wrap = this.inputEl().wrap({
26329             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26330         });
26331         
26332         this.editorcore.onRender(ct, position);
26333          
26334         if (this.resizable) {
26335             this.resizeEl = new Roo.Resizable(this.wrap, {
26336                 pinned : true,
26337                 wrap: true,
26338                 dynamic : true,
26339                 minHeight : this.height,
26340                 height: this.height,
26341                 handles : this.resizable,
26342                 width: this.width,
26343                 listeners : {
26344                     resize : function(r, w, h) {
26345                         _t.onResize(w,h); // -something
26346                     }
26347                 }
26348             });
26349             
26350         }
26351         this.createToolbar(this);
26352        
26353         
26354         if(!this.width && this.resizable){
26355             this.setSize(this.wrap.getSize());
26356         }
26357         if (this.resizeEl) {
26358             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26359             // should trigger onReize..
26360         }
26361         
26362     },
26363
26364     // private
26365     onResize : function(w, h)
26366     {
26367         Roo.log('resize: ' +w + ',' + h );
26368         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26369         var ew = false;
26370         var eh = false;
26371         
26372         if(this.inputEl() ){
26373             if(typeof w == 'number'){
26374                 var aw = w - this.wrap.getFrameWidth('lr');
26375                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26376                 ew = aw;
26377             }
26378             if(typeof h == 'number'){
26379                  var tbh = -11;  // fixme it needs to tool bar size!
26380                 for (var i =0; i < this.toolbars.length;i++) {
26381                     // fixme - ask toolbars for heights?
26382                     tbh += this.toolbars[i].el.getHeight();
26383                     //if (this.toolbars[i].footer) {
26384                     //    tbh += this.toolbars[i].footer.el.getHeight();
26385                     //}
26386                 }
26387               
26388                 
26389                 
26390                 
26391                 
26392                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26393                 ah -= 5; // knock a few pixes off for look..
26394                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26395                 var eh = ah;
26396             }
26397         }
26398         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26399         this.editorcore.onResize(ew,eh);
26400         
26401     },
26402
26403     /**
26404      * Toggles the editor between standard and source edit mode.
26405      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26406      */
26407     toggleSourceEdit : function(sourceEditMode)
26408     {
26409         this.editorcore.toggleSourceEdit(sourceEditMode);
26410         
26411         if(this.editorcore.sourceEditMode){
26412             Roo.log('editor - showing textarea');
26413             
26414 //            Roo.log('in');
26415 //            Roo.log(this.syncValue());
26416             this.syncValue();
26417             this.inputEl().removeClass(['hide', 'x-hidden']);
26418             this.inputEl().dom.removeAttribute('tabIndex');
26419             this.inputEl().focus();
26420         }else{
26421             Roo.log('editor - hiding textarea');
26422 //            Roo.log('out')
26423 //            Roo.log(this.pushValue()); 
26424             this.pushValue();
26425             
26426             this.inputEl().addClass(['hide', 'x-hidden']);
26427             this.inputEl().dom.setAttribute('tabIndex', -1);
26428             //this.deferFocus();
26429         }
26430          
26431         if(this.resizable){
26432             this.setSize(this.wrap.getSize());
26433         }
26434         
26435         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26436     },
26437  
26438     // private (for BoxComponent)
26439     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26440
26441     // private (for BoxComponent)
26442     getResizeEl : function(){
26443         return this.wrap;
26444     },
26445
26446     // private (for BoxComponent)
26447     getPositionEl : function(){
26448         return this.wrap;
26449     },
26450
26451     // private
26452     initEvents : function(){
26453         this.originalValue = this.getValue();
26454     },
26455
26456 //    /**
26457 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26458 //     * @method
26459 //     */
26460 //    markInvalid : Roo.emptyFn,
26461 //    /**
26462 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26463 //     * @method
26464 //     */
26465 //    clearInvalid : Roo.emptyFn,
26466
26467     setValue : function(v){
26468         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26469         this.editorcore.pushValue();
26470     },
26471
26472      
26473     // private
26474     deferFocus : function(){
26475         this.focus.defer(10, this);
26476     },
26477
26478     // doc'ed in Field
26479     focus : function(){
26480         this.editorcore.focus();
26481         
26482     },
26483       
26484
26485     // private
26486     onDestroy : function(){
26487         
26488         
26489         
26490         if(this.rendered){
26491             
26492             for (var i =0; i < this.toolbars.length;i++) {
26493                 // fixme - ask toolbars for heights?
26494                 this.toolbars[i].onDestroy();
26495             }
26496             
26497             this.wrap.dom.innerHTML = '';
26498             this.wrap.remove();
26499         }
26500     },
26501
26502     // private
26503     onFirstFocus : function(){
26504         //Roo.log("onFirstFocus");
26505         this.editorcore.onFirstFocus();
26506          for (var i =0; i < this.toolbars.length;i++) {
26507             this.toolbars[i].onFirstFocus();
26508         }
26509         
26510     },
26511     
26512     // private
26513     syncValue : function()
26514     {   
26515         this.editorcore.syncValue();
26516     },
26517     
26518     pushValue : function()
26519     {   
26520         this.editorcore.pushValue();
26521     }
26522      
26523     
26524     // hide stuff that is not compatible
26525     /**
26526      * @event blur
26527      * @hide
26528      */
26529     /**
26530      * @event change
26531      * @hide
26532      */
26533     /**
26534      * @event focus
26535      * @hide
26536      */
26537     /**
26538      * @event specialkey
26539      * @hide
26540      */
26541     /**
26542      * @cfg {String} fieldClass @hide
26543      */
26544     /**
26545      * @cfg {String} focusClass @hide
26546      */
26547     /**
26548      * @cfg {String} autoCreate @hide
26549      */
26550     /**
26551      * @cfg {String} inputType @hide
26552      */
26553      
26554     /**
26555      * @cfg {String} invalidText @hide
26556      */
26557     /**
26558      * @cfg {String} msgFx @hide
26559      */
26560     /**
26561      * @cfg {String} validateOnBlur @hide
26562      */
26563 });
26564  
26565     
26566    
26567    
26568    
26569       
26570 Roo.namespace('Roo.bootstrap.htmleditor');
26571 /**
26572  * @class Roo.bootstrap.HtmlEditorToolbar1
26573  * Basic Toolbar
26574  * 
26575  * @example
26576  * Usage:
26577  *
26578  new Roo.bootstrap.HtmlEditor({
26579     ....
26580     toolbars : [
26581         new Roo.bootstrap.HtmlEditorToolbar1({
26582             disable : { fonts: 1 , format: 1, ..., ... , ...],
26583             btns : [ .... ]
26584         })
26585     }
26586      
26587  * 
26588  * @cfg {Object} disable List of elements to disable..
26589  * @cfg {Array} btns List of additional buttons.
26590  * 
26591  * 
26592  * NEEDS Extra CSS? 
26593  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26594  */
26595  
26596 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26597 {
26598     
26599     Roo.apply(this, config);
26600     
26601     // default disabled, based on 'good practice'..
26602     this.disable = this.disable || {};
26603     Roo.applyIf(this.disable, {
26604         fontSize : true,
26605         colors : true,
26606         specialElements : true
26607     });
26608     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26609     
26610     this.editor = config.editor;
26611     this.editorcore = config.editor.editorcore;
26612     
26613     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26614     
26615     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26616     // dont call parent... till later.
26617 }
26618 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26619      
26620     bar : true,
26621     
26622     editor : false,
26623     editorcore : false,
26624     
26625     
26626     formats : [
26627         "p" ,  
26628         "h1","h2","h3","h4","h5","h6", 
26629         "pre", "code", 
26630         "abbr", "acronym", "address", "cite", "samp", "var",
26631         'div','span'
26632     ],
26633     
26634     onRender : function(ct, position)
26635     {
26636        // Roo.log("Call onRender: " + this.xtype);
26637         
26638        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26639        Roo.log(this.el);
26640        this.el.dom.style.marginBottom = '0';
26641        var _this = this;
26642        var editorcore = this.editorcore;
26643        var editor= this.editor;
26644        
26645        var children = [];
26646        var btn = function(id,cmd , toggle, handler, html){
26647        
26648             var  event = toggle ? 'toggle' : 'click';
26649        
26650             var a = {
26651                 size : 'sm',
26652                 xtype: 'Button',
26653                 xns: Roo.bootstrap,
26654                 //glyphicon : id,
26655                 fa: id,
26656                 cmd : id || cmd,
26657                 enableToggle:toggle !== false,
26658                 html : html || '',
26659                 pressed : toggle ? false : null,
26660                 listeners : {}
26661             };
26662             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26663                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26664             };
26665             children.push(a);
26666             return a;
26667        }
26668        
26669     //    var cb_box = function...
26670         
26671         var style = {
26672                 xtype: 'Button',
26673                 size : 'sm',
26674                 xns: Roo.bootstrap,
26675                 fa : 'font',
26676                 //html : 'submit'
26677                 menu : {
26678                     xtype: 'Menu',
26679                     xns: Roo.bootstrap,
26680                     items:  []
26681                 }
26682         };
26683         Roo.each(this.formats, function(f) {
26684             style.menu.items.push({
26685                 xtype :'MenuItem',
26686                 xns: Roo.bootstrap,
26687                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26688                 tagname : f,
26689                 listeners : {
26690                     click : function()
26691                     {
26692                         editorcore.insertTag(this.tagname);
26693                         editor.focus();
26694                     }
26695                 }
26696                 
26697             });
26698         });
26699         children.push(style);   
26700         
26701         btn('bold',false,true);
26702         btn('italic',false,true);
26703         btn('align-left', 'justifyleft',true);
26704         btn('align-center', 'justifycenter',true);
26705         btn('align-right' , 'justifyright',true);
26706         btn('link', false, false, function(btn) {
26707             //Roo.log("create link?");
26708             var url = prompt(this.createLinkText, this.defaultLinkValue);
26709             if(url && url != 'http:/'+'/'){
26710                 this.editorcore.relayCmd('createlink', url);
26711             }
26712         }),
26713         btn('list','insertunorderedlist',true);
26714         btn('pencil', false,true, function(btn){
26715                 Roo.log(this);
26716                 this.toggleSourceEdit(btn.pressed);
26717         });
26718         
26719         if (this.editor.btns.length > 0) {
26720             for (var i = 0; i<this.editor.btns.length; i++) {
26721                 children.push(this.editor.btns[i]);
26722             }
26723         }
26724         
26725         /*
26726         var cog = {
26727                 xtype: 'Button',
26728                 size : 'sm',
26729                 xns: Roo.bootstrap,
26730                 glyphicon : 'cog',
26731                 //html : 'submit'
26732                 menu : {
26733                     xtype: 'Menu',
26734                     xns: Roo.bootstrap,
26735                     items:  []
26736                 }
26737         };
26738         
26739         cog.menu.items.push({
26740             xtype :'MenuItem',
26741             xns: Roo.bootstrap,
26742             html : Clean styles,
26743             tagname : f,
26744             listeners : {
26745                 click : function()
26746                 {
26747                     editorcore.insertTag(this.tagname);
26748                     editor.focus();
26749                 }
26750             }
26751             
26752         });
26753        */
26754         
26755          
26756        this.xtype = 'NavSimplebar';
26757         
26758         for(var i=0;i< children.length;i++) {
26759             
26760             this.buttons.add(this.addxtypeChild(children[i]));
26761             
26762         }
26763         
26764         editor.on('editorevent', this.updateToolbar, this);
26765     },
26766     onBtnClick : function(id)
26767     {
26768        this.editorcore.relayCmd(id);
26769        this.editorcore.focus();
26770     },
26771     
26772     /**
26773      * Protected method that will not generally be called directly. It triggers
26774      * a toolbar update by reading the markup state of the current selection in the editor.
26775      */
26776     updateToolbar: function(){
26777
26778         if(!this.editorcore.activated){
26779             this.editor.onFirstFocus(); // is this neeed?
26780             return;
26781         }
26782
26783         var btns = this.buttons; 
26784         var doc = this.editorcore.doc;
26785         btns.get('bold').setActive(doc.queryCommandState('bold'));
26786         btns.get('italic').setActive(doc.queryCommandState('italic'));
26787         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26788         
26789         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26790         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26791         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26792         
26793         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26794         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26795          /*
26796         
26797         var ans = this.editorcore.getAllAncestors();
26798         if (this.formatCombo) {
26799             
26800             
26801             var store = this.formatCombo.store;
26802             this.formatCombo.setValue("");
26803             for (var i =0; i < ans.length;i++) {
26804                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26805                     // select it..
26806                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26807                     break;
26808                 }
26809             }
26810         }
26811         
26812         
26813         
26814         // hides menus... - so this cant be on a menu...
26815         Roo.bootstrap.MenuMgr.hideAll();
26816         */
26817         Roo.bootstrap.MenuMgr.hideAll();
26818         //this.editorsyncValue();
26819     },
26820     onFirstFocus: function() {
26821         this.buttons.each(function(item){
26822            item.enable();
26823         });
26824     },
26825     toggleSourceEdit : function(sourceEditMode){
26826         
26827           
26828         if(sourceEditMode){
26829             Roo.log("disabling buttons");
26830            this.buttons.each( function(item){
26831                 if(item.cmd != 'pencil'){
26832                     item.disable();
26833                 }
26834             });
26835           
26836         }else{
26837             Roo.log("enabling buttons");
26838             if(this.editorcore.initialized){
26839                 this.buttons.each( function(item){
26840                     item.enable();
26841                 });
26842             }
26843             
26844         }
26845         Roo.log("calling toggole on editor");
26846         // tell the editor that it's been pressed..
26847         this.editor.toggleSourceEdit(sourceEditMode);
26848        
26849     }
26850 });
26851
26852
26853
26854
26855  
26856 /*
26857  * - LGPL
26858  */
26859
26860 /**
26861  * @class Roo.bootstrap.Markdown
26862  * @extends Roo.bootstrap.TextArea
26863  * Bootstrap Showdown editable area
26864  * @cfg {string} content
26865  * 
26866  * @constructor
26867  * Create a new Showdown
26868  */
26869
26870 Roo.bootstrap.Markdown = function(config){
26871     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26872    
26873 };
26874
26875 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26876     
26877     editing :false,
26878     
26879     initEvents : function()
26880     {
26881         
26882         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26883         this.markdownEl = this.el.createChild({
26884             cls : 'roo-markdown-area'
26885         });
26886         this.inputEl().addClass('d-none');
26887         if (this.getValue() == '') {
26888             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26889             
26890         } else {
26891             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26892         }
26893         this.markdownEl.on('click', this.toggleTextEdit, this);
26894         this.on('blur', this.toggleTextEdit, this);
26895         this.on('specialkey', this.resizeTextArea, this);
26896     },
26897     
26898     toggleTextEdit : function()
26899     {
26900         var sh = this.markdownEl.getHeight();
26901         this.inputEl().addClass('d-none');
26902         this.markdownEl.addClass('d-none');
26903         if (!this.editing) {
26904             // show editor?
26905             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26906             this.inputEl().removeClass('d-none');
26907             this.inputEl().focus();
26908             this.editing = true;
26909             return;
26910         }
26911         // show showdown...
26912         this.updateMarkdown();
26913         this.markdownEl.removeClass('d-none');
26914         this.editing = false;
26915         return;
26916     },
26917     updateMarkdown : function()
26918     {
26919         if (this.getValue() == '') {
26920             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26921             return;
26922         }
26923  
26924         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26925     },
26926     
26927     resizeTextArea: function () {
26928         
26929         var sh = 100;
26930         Roo.log([sh, this.getValue().split("\n").length * 30]);
26931         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26932     },
26933     setValue : function(val)
26934     {
26935         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26936         if (!this.editing) {
26937             this.updateMarkdown();
26938         }
26939         
26940     },
26941     focus : function()
26942     {
26943         if (!this.editing) {
26944             this.toggleTextEdit();
26945         }
26946         
26947     }
26948
26949
26950 });
26951 /**
26952  * @class Roo.bootstrap.Table.AbstractSelectionModel
26953  * @extends Roo.util.Observable
26954  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26955  * implemented by descendant classes.  This class should not be directly instantiated.
26956  * @constructor
26957  */
26958 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26959     this.locked = false;
26960     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26961 };
26962
26963
26964 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26965     /** @ignore Called by the grid automatically. Do not call directly. */
26966     init : function(grid){
26967         this.grid = grid;
26968         this.initEvents();
26969     },
26970
26971     /**
26972      * Locks the selections.
26973      */
26974     lock : function(){
26975         this.locked = true;
26976     },
26977
26978     /**
26979      * Unlocks the selections.
26980      */
26981     unlock : function(){
26982         this.locked = false;
26983     },
26984
26985     /**
26986      * Returns true if the selections are locked.
26987      * @return {Boolean}
26988      */
26989     isLocked : function(){
26990         return this.locked;
26991     },
26992     
26993     
26994     initEvents : function ()
26995     {
26996         
26997     }
26998 });
26999 /**
27000  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27001  * @class Roo.bootstrap.Table.RowSelectionModel
27002  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27003  * It supports multiple selections and keyboard selection/navigation. 
27004  * @constructor
27005  * @param {Object} config
27006  */
27007
27008 Roo.bootstrap.Table.RowSelectionModel = function(config){
27009     Roo.apply(this, config);
27010     this.selections = new Roo.util.MixedCollection(false, function(o){
27011         return o.id;
27012     });
27013
27014     this.last = false;
27015     this.lastActive = false;
27016
27017     this.addEvents({
27018         /**
27019              * @event selectionchange
27020              * Fires when the selection changes
27021              * @param {SelectionModel} this
27022              */
27023             "selectionchange" : true,
27024         /**
27025              * @event afterselectionchange
27026              * Fires after the selection changes (eg. by key press or clicking)
27027              * @param {SelectionModel} this
27028              */
27029             "afterselectionchange" : true,
27030         /**
27031              * @event beforerowselect
27032              * Fires when a row is selected being selected, return false to cancel.
27033              * @param {SelectionModel} this
27034              * @param {Number} rowIndex The selected index
27035              * @param {Boolean} keepExisting False if other selections will be cleared
27036              */
27037             "beforerowselect" : true,
27038         /**
27039              * @event rowselect
27040              * Fires when a row is selected.
27041              * @param {SelectionModel} this
27042              * @param {Number} rowIndex The selected index
27043              * @param {Roo.data.Record} r The record
27044              */
27045             "rowselect" : true,
27046         /**
27047              * @event rowdeselect
27048              * Fires when a row is deselected.
27049              * @param {SelectionModel} this
27050              * @param {Number} rowIndex The selected index
27051              */
27052         "rowdeselect" : true
27053     });
27054     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27055     this.locked = false;
27056  };
27057
27058 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27059     /**
27060      * @cfg {Boolean} singleSelect
27061      * True to allow selection of only one row at a time (defaults to false)
27062      */
27063     singleSelect : false,
27064
27065     // private
27066     initEvents : function()
27067     {
27068
27069         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27070         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27071         //}else{ // allow click to work like normal
27072          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27073         //}
27074         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27075         this.grid.on("rowclick", this.handleMouseDown, this);
27076         
27077         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27078             "up" : function(e){
27079                 if(!e.shiftKey){
27080                     this.selectPrevious(e.shiftKey);
27081                 }else if(this.last !== false && this.lastActive !== false){
27082                     var last = this.last;
27083                     this.selectRange(this.last,  this.lastActive-1);
27084                     this.grid.getView().focusRow(this.lastActive);
27085                     if(last !== false){
27086                         this.last = last;
27087                     }
27088                 }else{
27089                     this.selectFirstRow();
27090                 }
27091                 this.fireEvent("afterselectionchange", this);
27092             },
27093             "down" : function(e){
27094                 if(!e.shiftKey){
27095                     this.selectNext(e.shiftKey);
27096                 }else if(this.last !== false && this.lastActive !== false){
27097                     var last = this.last;
27098                     this.selectRange(this.last,  this.lastActive+1);
27099                     this.grid.getView().focusRow(this.lastActive);
27100                     if(last !== false){
27101                         this.last = last;
27102                     }
27103                 }else{
27104                     this.selectFirstRow();
27105                 }
27106                 this.fireEvent("afterselectionchange", this);
27107             },
27108             scope: this
27109         });
27110         this.grid.store.on('load', function(){
27111             this.selections.clear();
27112         },this);
27113         /*
27114         var view = this.grid.view;
27115         view.on("refresh", this.onRefresh, this);
27116         view.on("rowupdated", this.onRowUpdated, this);
27117         view.on("rowremoved", this.onRemove, this);
27118         */
27119     },
27120
27121     // private
27122     onRefresh : function()
27123     {
27124         var ds = this.grid.store, i, v = this.grid.view;
27125         var s = this.selections;
27126         s.each(function(r){
27127             if((i = ds.indexOfId(r.id)) != -1){
27128                 v.onRowSelect(i);
27129             }else{
27130                 s.remove(r);
27131             }
27132         });
27133     },
27134
27135     // private
27136     onRemove : function(v, index, r){
27137         this.selections.remove(r);
27138     },
27139
27140     // private
27141     onRowUpdated : function(v, index, r){
27142         if(this.isSelected(r)){
27143             v.onRowSelect(index);
27144         }
27145     },
27146
27147     /**
27148      * Select records.
27149      * @param {Array} records The records to select
27150      * @param {Boolean} keepExisting (optional) True to keep existing selections
27151      */
27152     selectRecords : function(records, keepExisting)
27153     {
27154         if(!keepExisting){
27155             this.clearSelections();
27156         }
27157             var ds = this.grid.store;
27158         for(var i = 0, len = records.length; i < len; i++){
27159             this.selectRow(ds.indexOf(records[i]), true);
27160         }
27161     },
27162
27163     /**
27164      * Gets the number of selected rows.
27165      * @return {Number}
27166      */
27167     getCount : function(){
27168         return this.selections.length;
27169     },
27170
27171     /**
27172      * Selects the first row in the grid.
27173      */
27174     selectFirstRow : function(){
27175         this.selectRow(0);
27176     },
27177
27178     /**
27179      * Select the last row.
27180      * @param {Boolean} keepExisting (optional) True to keep existing selections
27181      */
27182     selectLastRow : function(keepExisting){
27183         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27184         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27185     },
27186
27187     /**
27188      * Selects the row immediately following the last selected row.
27189      * @param {Boolean} keepExisting (optional) True to keep existing selections
27190      */
27191     selectNext : function(keepExisting)
27192     {
27193             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27194             this.selectRow(this.last+1, keepExisting);
27195             this.grid.getView().focusRow(this.last);
27196         }
27197     },
27198
27199     /**
27200      * Selects the row that precedes the last selected row.
27201      * @param {Boolean} keepExisting (optional) True to keep existing selections
27202      */
27203     selectPrevious : function(keepExisting){
27204         if(this.last){
27205             this.selectRow(this.last-1, keepExisting);
27206             this.grid.getView().focusRow(this.last);
27207         }
27208     },
27209
27210     /**
27211      * Returns the selected records
27212      * @return {Array} Array of selected records
27213      */
27214     getSelections : function(){
27215         return [].concat(this.selections.items);
27216     },
27217
27218     /**
27219      * Returns the first selected record.
27220      * @return {Record}
27221      */
27222     getSelected : function(){
27223         return this.selections.itemAt(0);
27224     },
27225
27226
27227     /**
27228      * Clears all selections.
27229      */
27230     clearSelections : function(fast)
27231     {
27232         if(this.locked) {
27233             return;
27234         }
27235         if(fast !== true){
27236                 var ds = this.grid.store;
27237             var s = this.selections;
27238             s.each(function(r){
27239                 this.deselectRow(ds.indexOfId(r.id));
27240             }, this);
27241             s.clear();
27242         }else{
27243             this.selections.clear();
27244         }
27245         this.last = false;
27246     },
27247
27248
27249     /**
27250      * Selects all rows.
27251      */
27252     selectAll : function(){
27253         if(this.locked) {
27254             return;
27255         }
27256         this.selections.clear();
27257         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27258             this.selectRow(i, true);
27259         }
27260     },
27261
27262     /**
27263      * Returns True if there is a selection.
27264      * @return {Boolean}
27265      */
27266     hasSelection : function(){
27267         return this.selections.length > 0;
27268     },
27269
27270     /**
27271      * Returns True if the specified row is selected.
27272      * @param {Number/Record} record The record or index of the record to check
27273      * @return {Boolean}
27274      */
27275     isSelected : function(index){
27276             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27277         return (r && this.selections.key(r.id) ? true : false);
27278     },
27279
27280     /**
27281      * Returns True if the specified record id is selected.
27282      * @param {String} id The id of record to check
27283      * @return {Boolean}
27284      */
27285     isIdSelected : function(id){
27286         return (this.selections.key(id) ? true : false);
27287     },
27288
27289
27290     // private
27291     handleMouseDBClick : function(e, t){
27292         
27293     },
27294     // private
27295     handleMouseDown : function(e, t)
27296     {
27297             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27298         if(this.isLocked() || rowIndex < 0 ){
27299             return;
27300         };
27301         if(e.shiftKey && this.last !== false){
27302             var last = this.last;
27303             this.selectRange(last, rowIndex, e.ctrlKey);
27304             this.last = last; // reset the last
27305             t.focus();
27306     
27307         }else{
27308             var isSelected = this.isSelected(rowIndex);
27309             //Roo.log("select row:" + rowIndex);
27310             if(isSelected){
27311                 this.deselectRow(rowIndex);
27312             } else {
27313                         this.selectRow(rowIndex, true);
27314             }
27315     
27316             /*
27317                 if(e.button !== 0 && isSelected){
27318                 alert('rowIndex 2: ' + rowIndex);
27319                     view.focusRow(rowIndex);
27320                 }else if(e.ctrlKey && isSelected){
27321                     this.deselectRow(rowIndex);
27322                 }else if(!isSelected){
27323                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27324                     view.focusRow(rowIndex);
27325                 }
27326             */
27327         }
27328         this.fireEvent("afterselectionchange", this);
27329     },
27330     // private
27331     handleDragableRowClick :  function(grid, rowIndex, e) 
27332     {
27333         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27334             this.selectRow(rowIndex, false);
27335             grid.view.focusRow(rowIndex);
27336              this.fireEvent("afterselectionchange", this);
27337         }
27338     },
27339     
27340     /**
27341      * Selects multiple rows.
27342      * @param {Array} rows Array of the indexes of the row to select
27343      * @param {Boolean} keepExisting (optional) True to keep existing selections
27344      */
27345     selectRows : function(rows, keepExisting){
27346         if(!keepExisting){
27347             this.clearSelections();
27348         }
27349         for(var i = 0, len = rows.length; i < len; i++){
27350             this.selectRow(rows[i], true);
27351         }
27352     },
27353
27354     /**
27355      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27356      * @param {Number} startRow The index of the first row in the range
27357      * @param {Number} endRow The index of the last row in the range
27358      * @param {Boolean} keepExisting (optional) True to retain existing selections
27359      */
27360     selectRange : function(startRow, endRow, keepExisting){
27361         if(this.locked) {
27362             return;
27363         }
27364         if(!keepExisting){
27365             this.clearSelections();
27366         }
27367         if(startRow <= endRow){
27368             for(var i = startRow; i <= endRow; i++){
27369                 this.selectRow(i, true);
27370             }
27371         }else{
27372             for(var i = startRow; i >= endRow; i--){
27373                 this.selectRow(i, true);
27374             }
27375         }
27376     },
27377
27378     /**
27379      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27380      * @param {Number} startRow The index of the first row in the range
27381      * @param {Number} endRow The index of the last row in the range
27382      */
27383     deselectRange : function(startRow, endRow, preventViewNotify){
27384         if(this.locked) {
27385             return;
27386         }
27387         for(var i = startRow; i <= endRow; i++){
27388             this.deselectRow(i, preventViewNotify);
27389         }
27390     },
27391
27392     /**
27393      * Selects a row.
27394      * @param {Number} row The index of the row to select
27395      * @param {Boolean} keepExisting (optional) True to keep existing selections
27396      */
27397     selectRow : function(index, keepExisting, preventViewNotify)
27398     {
27399             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27400             return;
27401         }
27402         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27403             if(!keepExisting || this.singleSelect){
27404                 this.clearSelections();
27405             }
27406             
27407             var r = this.grid.store.getAt(index);
27408             //console.log('selectRow - record id :' + r.id);
27409             
27410             this.selections.add(r);
27411             this.last = this.lastActive = index;
27412             if(!preventViewNotify){
27413                 var proxy = new Roo.Element(
27414                                 this.grid.getRowDom(index)
27415                 );
27416                 proxy.addClass('bg-info info');
27417             }
27418             this.fireEvent("rowselect", this, index, r);
27419             this.fireEvent("selectionchange", this);
27420         }
27421     },
27422
27423     /**
27424      * Deselects a row.
27425      * @param {Number} row The index of the row to deselect
27426      */
27427     deselectRow : function(index, preventViewNotify)
27428     {
27429         if(this.locked) {
27430             return;
27431         }
27432         if(this.last == index){
27433             this.last = false;
27434         }
27435         if(this.lastActive == index){
27436             this.lastActive = false;
27437         }
27438         
27439         var r = this.grid.store.getAt(index);
27440         if (!r) {
27441             return;
27442         }
27443         
27444         this.selections.remove(r);
27445         //.console.log('deselectRow - record id :' + r.id);
27446         if(!preventViewNotify){
27447         
27448             var proxy = new Roo.Element(
27449                 this.grid.getRowDom(index)
27450             );
27451             proxy.removeClass('bg-info info');
27452         }
27453         this.fireEvent("rowdeselect", this, index);
27454         this.fireEvent("selectionchange", this);
27455     },
27456
27457     // private
27458     restoreLast : function(){
27459         if(this._last){
27460             this.last = this._last;
27461         }
27462     },
27463
27464     // private
27465     acceptsNav : function(row, col, cm){
27466         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27467     },
27468
27469     // private
27470     onEditorKey : function(field, e){
27471         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27472         if(k == e.TAB){
27473             e.stopEvent();
27474             ed.completeEdit();
27475             if(e.shiftKey){
27476                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27477             }else{
27478                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27479             }
27480         }else if(k == e.ENTER && !e.ctrlKey){
27481             e.stopEvent();
27482             ed.completeEdit();
27483             if(e.shiftKey){
27484                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27485             }else{
27486                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27487             }
27488         }else if(k == e.ESC){
27489             ed.cancelEdit();
27490         }
27491         if(newCell){
27492             g.startEditing(newCell[0], newCell[1]);
27493         }
27494     }
27495 });
27496 /*
27497  * Based on:
27498  * Ext JS Library 1.1.1
27499  * Copyright(c) 2006-2007, Ext JS, LLC.
27500  *
27501  * Originally Released Under LGPL - original licence link has changed is not relivant.
27502  *
27503  * Fork - LGPL
27504  * <script type="text/javascript">
27505  */
27506  
27507 /**
27508  * @class Roo.bootstrap.PagingToolbar
27509  * @extends Roo.bootstrap.NavSimplebar
27510  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27511  * @constructor
27512  * Create a new PagingToolbar
27513  * @param {Object} config The config object
27514  * @param {Roo.data.Store} store
27515  */
27516 Roo.bootstrap.PagingToolbar = function(config)
27517 {
27518     // old args format still supported... - xtype is prefered..
27519         // created from xtype...
27520     
27521     this.ds = config.dataSource;
27522     
27523     if (config.store && !this.ds) {
27524         this.store= Roo.factory(config.store, Roo.data);
27525         this.ds = this.store;
27526         this.ds.xmodule = this.xmodule || false;
27527     }
27528     
27529     this.toolbarItems = [];
27530     if (config.items) {
27531         this.toolbarItems = config.items;
27532     }
27533     
27534     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27535     
27536     this.cursor = 0;
27537     
27538     if (this.ds) { 
27539         this.bind(this.ds);
27540     }
27541     
27542     if (Roo.bootstrap.version == 4) {
27543         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27544     } else {
27545         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27546     }
27547     
27548 };
27549
27550 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27551     /**
27552      * @cfg {Roo.data.Store} dataSource
27553      * The underlying data store providing the paged data
27554      */
27555     /**
27556      * @cfg {String/HTMLElement/Element} container
27557      * container The id or element that will contain the toolbar
27558      */
27559     /**
27560      * @cfg {Boolean} displayInfo
27561      * True to display the displayMsg (defaults to false)
27562      */
27563     /**
27564      * @cfg {Number} pageSize
27565      * The number of records to display per page (defaults to 20)
27566      */
27567     pageSize: 20,
27568     /**
27569      * @cfg {String} displayMsg
27570      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27571      */
27572     displayMsg : 'Displaying {0} - {1} of {2}',
27573     /**
27574      * @cfg {String} emptyMsg
27575      * The message to display when no records are found (defaults to "No data to display")
27576      */
27577     emptyMsg : 'No data to display',
27578     /**
27579      * Customizable piece of the default paging text (defaults to "Page")
27580      * @type String
27581      */
27582     beforePageText : "Page",
27583     /**
27584      * Customizable piece of the default paging text (defaults to "of %0")
27585      * @type String
27586      */
27587     afterPageText : "of {0}",
27588     /**
27589      * Customizable piece of the default paging text (defaults to "First Page")
27590      * @type String
27591      */
27592     firstText : "First Page",
27593     /**
27594      * Customizable piece of the default paging text (defaults to "Previous Page")
27595      * @type String
27596      */
27597     prevText : "Previous Page",
27598     /**
27599      * Customizable piece of the default paging text (defaults to "Next Page")
27600      * @type String
27601      */
27602     nextText : "Next Page",
27603     /**
27604      * Customizable piece of the default paging text (defaults to "Last Page")
27605      * @type String
27606      */
27607     lastText : "Last Page",
27608     /**
27609      * Customizable piece of the default paging text (defaults to "Refresh")
27610      * @type String
27611      */
27612     refreshText : "Refresh",
27613
27614     buttons : false,
27615     // private
27616     onRender : function(ct, position) 
27617     {
27618         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27619         this.navgroup.parentId = this.id;
27620         this.navgroup.onRender(this.el, null);
27621         // add the buttons to the navgroup
27622         
27623         if(this.displayInfo){
27624             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27625             this.displayEl = this.el.select('.x-paging-info', true).first();
27626 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27627 //            this.displayEl = navel.el.select('span',true).first();
27628         }
27629         
27630         var _this = this;
27631         
27632         if(this.buttons){
27633             Roo.each(_this.buttons, function(e){ // this might need to use render????
27634                Roo.factory(e).render(_this.el);
27635             });
27636         }
27637             
27638         Roo.each(_this.toolbarItems, function(e) {
27639             _this.navgroup.addItem(e);
27640         });
27641         
27642         
27643         this.first = this.navgroup.addItem({
27644             tooltip: this.firstText,
27645             cls: "prev btn-outline-secondary",
27646             html : ' <i class="fa fa-step-backward"></i>',
27647             disabled: true,
27648             preventDefault: true,
27649             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27650         });
27651         
27652         this.prev =  this.navgroup.addItem({
27653             tooltip: this.prevText,
27654             cls: "prev btn-outline-secondary",
27655             html : ' <i class="fa fa-backward"></i>',
27656             disabled: true,
27657             preventDefault: true,
27658             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27659         });
27660     //this.addSeparator();
27661         
27662         
27663         var field = this.navgroup.addItem( {
27664             tagtype : 'span',
27665             cls : 'x-paging-position  btn-outline-secondary',
27666              disabled: true,
27667             html : this.beforePageText  +
27668                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27669                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27670          } ); //?? escaped?
27671         
27672         this.field = field.el.select('input', true).first();
27673         this.field.on("keydown", this.onPagingKeydown, this);
27674         this.field.on("focus", function(){this.dom.select();});
27675     
27676     
27677         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27678         //this.field.setHeight(18);
27679         //this.addSeparator();
27680         this.next = this.navgroup.addItem({
27681             tooltip: this.nextText,
27682             cls: "next btn-outline-secondary",
27683             html : ' <i class="fa fa-forward"></i>',
27684             disabled: true,
27685             preventDefault: true,
27686             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27687         });
27688         this.last = this.navgroup.addItem({
27689             tooltip: this.lastText,
27690             html : ' <i class="fa fa-step-forward"></i>',
27691             cls: "next btn-outline-secondary",
27692             disabled: true,
27693             preventDefault: true,
27694             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27695         });
27696     //this.addSeparator();
27697         this.loading = this.navgroup.addItem({
27698             tooltip: this.refreshText,
27699             cls: "btn-outline-secondary",
27700             html : ' <i class="fa fa-refresh"></i>',
27701             preventDefault: true,
27702             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27703         });
27704         
27705     },
27706
27707     // private
27708     updateInfo : function(){
27709         if(this.displayEl){
27710             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27711             var msg = count == 0 ?
27712                 this.emptyMsg :
27713                 String.format(
27714                     this.displayMsg,
27715                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27716                 );
27717             this.displayEl.update(msg);
27718         }
27719     },
27720
27721     // private
27722     onLoad : function(ds, r, o)
27723     {
27724         this.cursor = o.params && o.params.start ? o.params.start : 0;
27725         
27726         var d = this.getPageData(),
27727             ap = d.activePage,
27728             ps = d.pages;
27729         
27730         
27731         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27732         this.field.dom.value = ap;
27733         this.first.setDisabled(ap == 1);
27734         this.prev.setDisabled(ap == 1);
27735         this.next.setDisabled(ap == ps);
27736         this.last.setDisabled(ap == ps);
27737         this.loading.enable();
27738         this.updateInfo();
27739     },
27740
27741     // private
27742     getPageData : function(){
27743         var total = this.ds.getTotalCount();
27744         return {
27745             total : total,
27746             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27747             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27748         };
27749     },
27750
27751     // private
27752     onLoadError : function(){
27753         this.loading.enable();
27754     },
27755
27756     // private
27757     onPagingKeydown : function(e){
27758         var k = e.getKey();
27759         var d = this.getPageData();
27760         if(k == e.RETURN){
27761             var v = this.field.dom.value, pageNum;
27762             if(!v || isNaN(pageNum = parseInt(v, 10))){
27763                 this.field.dom.value = d.activePage;
27764                 return;
27765             }
27766             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27767             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27768             e.stopEvent();
27769         }
27770         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))
27771         {
27772           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27773           this.field.dom.value = pageNum;
27774           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27775           e.stopEvent();
27776         }
27777         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27778         {
27779           var v = this.field.dom.value, pageNum; 
27780           var increment = (e.shiftKey) ? 10 : 1;
27781           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27782                 increment *= -1;
27783           }
27784           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27785             this.field.dom.value = d.activePage;
27786             return;
27787           }
27788           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27789           {
27790             this.field.dom.value = parseInt(v, 10) + increment;
27791             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27792             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27793           }
27794           e.stopEvent();
27795         }
27796     },
27797
27798     // private
27799     beforeLoad : function(){
27800         if(this.loading){
27801             this.loading.disable();
27802         }
27803     },
27804
27805     // private
27806     onClick : function(which){
27807         
27808         var ds = this.ds;
27809         if (!ds) {
27810             return;
27811         }
27812         
27813         switch(which){
27814             case "first":
27815                 ds.load({params:{start: 0, limit: this.pageSize}});
27816             break;
27817             case "prev":
27818                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27819             break;
27820             case "next":
27821                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27822             break;
27823             case "last":
27824                 var total = ds.getTotalCount();
27825                 var extra = total % this.pageSize;
27826                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27827                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27828             break;
27829             case "refresh":
27830                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27831             break;
27832         }
27833     },
27834
27835     /**
27836      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27837      * @param {Roo.data.Store} store The data store to unbind
27838      */
27839     unbind : function(ds){
27840         ds.un("beforeload", this.beforeLoad, this);
27841         ds.un("load", this.onLoad, this);
27842         ds.un("loadexception", this.onLoadError, this);
27843         ds.un("remove", this.updateInfo, this);
27844         ds.un("add", this.updateInfo, this);
27845         this.ds = undefined;
27846     },
27847
27848     /**
27849      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27850      * @param {Roo.data.Store} store The data store to bind
27851      */
27852     bind : function(ds){
27853         ds.on("beforeload", this.beforeLoad, this);
27854         ds.on("load", this.onLoad, this);
27855         ds.on("loadexception", this.onLoadError, this);
27856         ds.on("remove", this.updateInfo, this);
27857         ds.on("add", this.updateInfo, this);
27858         this.ds = ds;
27859     }
27860 });/*
27861  * - LGPL
27862  *
27863  * element
27864  * 
27865  */
27866
27867 /**
27868  * @class Roo.bootstrap.MessageBar
27869  * @extends Roo.bootstrap.Component
27870  * Bootstrap MessageBar class
27871  * @cfg {String} html contents of the MessageBar
27872  * @cfg {String} weight (info | success | warning | danger) default info
27873  * @cfg {String} beforeClass insert the bar before the given class
27874  * @cfg {Boolean} closable (true | false) default false
27875  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27876  * 
27877  * @constructor
27878  * Create a new Element
27879  * @param {Object} config The config object
27880  */
27881
27882 Roo.bootstrap.MessageBar = function(config){
27883     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27884 };
27885
27886 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27887     
27888     html: '',
27889     weight: 'info',
27890     closable: false,
27891     fixed: false,
27892     beforeClass: 'bootstrap-sticky-wrap',
27893     
27894     getAutoCreate : function(){
27895         
27896         var cfg = {
27897             tag: 'div',
27898             cls: 'alert alert-dismissable alert-' + this.weight,
27899             cn: [
27900                 {
27901                     tag: 'span',
27902                     cls: 'message',
27903                     html: this.html || ''
27904                 }
27905             ]
27906         };
27907         
27908         if(this.fixed){
27909             cfg.cls += ' alert-messages-fixed';
27910         }
27911         
27912         if(this.closable){
27913             cfg.cn.push({
27914                 tag: 'button',
27915                 cls: 'close',
27916                 html: 'x'
27917             });
27918         }
27919         
27920         return cfg;
27921     },
27922     
27923     onRender : function(ct, position)
27924     {
27925         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27926         
27927         if(!this.el){
27928             var cfg = Roo.apply({},  this.getAutoCreate());
27929             cfg.id = Roo.id();
27930             
27931             if (this.cls) {
27932                 cfg.cls += ' ' + this.cls;
27933             }
27934             if (this.style) {
27935                 cfg.style = this.style;
27936             }
27937             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27938             
27939             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27940         }
27941         
27942         this.el.select('>button.close').on('click', this.hide, this);
27943         
27944     },
27945     
27946     show : function()
27947     {
27948         if (!this.rendered) {
27949             this.render();
27950         }
27951         
27952         this.el.show();
27953         
27954         this.fireEvent('show', this);
27955         
27956     },
27957     
27958     hide : function()
27959     {
27960         if (!this.rendered) {
27961             this.render();
27962         }
27963         
27964         this.el.hide();
27965         
27966         this.fireEvent('hide', this);
27967     },
27968     
27969     update : function()
27970     {
27971 //        var e = this.el.dom.firstChild;
27972 //        
27973 //        if(this.closable){
27974 //            e = e.nextSibling;
27975 //        }
27976 //        
27977 //        e.data = this.html || '';
27978
27979         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27980     }
27981    
27982 });
27983
27984  
27985
27986      /*
27987  * - LGPL
27988  *
27989  * Graph
27990  * 
27991  */
27992
27993
27994 /**
27995  * @class Roo.bootstrap.Graph
27996  * @extends Roo.bootstrap.Component
27997  * Bootstrap Graph class
27998 > Prameters
27999  -sm {number} sm 4
28000  -md {number} md 5
28001  @cfg {String} graphtype  bar | vbar | pie
28002  @cfg {number} g_x coodinator | centre x (pie)
28003  @cfg {number} g_y coodinator | centre y (pie)
28004  @cfg {number} g_r radius (pie)
28005  @cfg {number} g_height height of the chart (respected by all elements in the set)
28006  @cfg {number} g_width width of the chart (respected by all elements in the set)
28007  @cfg {Object} title The title of the chart
28008     
28009  -{Array}  values
28010  -opts (object) options for the chart 
28011      o {
28012      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28013      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28014      o vgutter (number)
28015      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.
28016      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28017      o to
28018      o stretch (boolean)
28019      o }
28020  -opts (object) options for the pie
28021      o{
28022      o cut
28023      o startAngle (number)
28024      o endAngle (number)
28025      } 
28026  *
28027  * @constructor
28028  * Create a new Input
28029  * @param {Object} config The config object
28030  */
28031
28032 Roo.bootstrap.Graph = function(config){
28033     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28034     
28035     this.addEvents({
28036         // img events
28037         /**
28038          * @event click
28039          * The img click event for the img.
28040          * @param {Roo.EventObject} e
28041          */
28042         "click" : true
28043     });
28044 };
28045
28046 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28047     
28048     sm: 4,
28049     md: 5,
28050     graphtype: 'bar',
28051     g_height: 250,
28052     g_width: 400,
28053     g_x: 50,
28054     g_y: 50,
28055     g_r: 30,
28056     opts:{
28057         //g_colors: this.colors,
28058         g_type: 'soft',
28059         g_gutter: '20%'
28060
28061     },
28062     title : false,
28063
28064     getAutoCreate : function(){
28065         
28066         var cfg = {
28067             tag: 'div',
28068             html : null
28069         };
28070         
28071         
28072         return  cfg;
28073     },
28074
28075     onRender : function(ct,position){
28076         
28077         
28078         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28079         
28080         if (typeof(Raphael) == 'undefined') {
28081             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28082             return;
28083         }
28084         
28085         this.raphael = Raphael(this.el.dom);
28086         
28087                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28088                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28089                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28090                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28091                 /*
28092                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28093                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28094                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28095                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28096                 
28097                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28098                 r.barchart(330, 10, 300, 220, data1);
28099                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28100                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28101                 */
28102                 
28103                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28104                 // r.barchart(30, 30, 560, 250,  xdata, {
28105                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28106                 //     axis : "0 0 1 1",
28107                 //     axisxlabels :  xdata
28108                 //     //yvalues : cols,
28109                    
28110                 // });
28111 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28112 //        
28113 //        this.load(null,xdata,{
28114 //                axis : "0 0 1 1",
28115 //                axisxlabels :  xdata
28116 //                });
28117
28118     },
28119
28120     load : function(graphtype,xdata,opts)
28121     {
28122         this.raphael.clear();
28123         if(!graphtype) {
28124             graphtype = this.graphtype;
28125         }
28126         if(!opts){
28127             opts = this.opts;
28128         }
28129         var r = this.raphael,
28130             fin = function () {
28131                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28132             },
28133             fout = function () {
28134                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28135             },
28136             pfin = function() {
28137                 this.sector.stop();
28138                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28139
28140                 if (this.label) {
28141                     this.label[0].stop();
28142                     this.label[0].attr({ r: 7.5 });
28143                     this.label[1].attr({ "font-weight": 800 });
28144                 }
28145             },
28146             pfout = function() {
28147                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28148
28149                 if (this.label) {
28150                     this.label[0].animate({ r: 5 }, 500, "bounce");
28151                     this.label[1].attr({ "font-weight": 400 });
28152                 }
28153             };
28154
28155         switch(graphtype){
28156             case 'bar':
28157                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28158                 break;
28159             case 'hbar':
28160                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28161                 break;
28162             case 'pie':
28163 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28164 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28165 //            
28166                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28167                 
28168                 break;
28169
28170         }
28171         
28172         if(this.title){
28173             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28174         }
28175         
28176     },
28177     
28178     setTitle: function(o)
28179     {
28180         this.title = o;
28181     },
28182     
28183     initEvents: function() {
28184         
28185         if(!this.href){
28186             this.el.on('click', this.onClick, this);
28187         }
28188     },
28189     
28190     onClick : function(e)
28191     {
28192         Roo.log('img onclick');
28193         this.fireEvent('click', this, e);
28194     }
28195    
28196 });
28197
28198  
28199 /*
28200  * - LGPL
28201  *
28202  * numberBox
28203  * 
28204  */
28205 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28206
28207 /**
28208  * @class Roo.bootstrap.dash.NumberBox
28209  * @extends Roo.bootstrap.Component
28210  * Bootstrap NumberBox class
28211  * @cfg {String} headline Box headline
28212  * @cfg {String} content Box content
28213  * @cfg {String} icon Box icon
28214  * @cfg {String} footer Footer text
28215  * @cfg {String} fhref Footer href
28216  * 
28217  * @constructor
28218  * Create a new NumberBox
28219  * @param {Object} config The config object
28220  */
28221
28222
28223 Roo.bootstrap.dash.NumberBox = function(config){
28224     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28225     
28226 };
28227
28228 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28229     
28230     headline : '',
28231     content : '',
28232     icon : '',
28233     footer : '',
28234     fhref : '',
28235     ficon : '',
28236     
28237     getAutoCreate : function(){
28238         
28239         var cfg = {
28240             tag : 'div',
28241             cls : 'small-box ',
28242             cn : [
28243                 {
28244                     tag : 'div',
28245                     cls : 'inner',
28246                     cn :[
28247                         {
28248                             tag : 'h3',
28249                             cls : 'roo-headline',
28250                             html : this.headline
28251                         },
28252                         {
28253                             tag : 'p',
28254                             cls : 'roo-content',
28255                             html : this.content
28256                         }
28257                     ]
28258                 }
28259             ]
28260         };
28261         
28262         if(this.icon){
28263             cfg.cn.push({
28264                 tag : 'div',
28265                 cls : 'icon',
28266                 cn :[
28267                     {
28268                         tag : 'i',
28269                         cls : 'ion ' + this.icon
28270                     }
28271                 ]
28272             });
28273         }
28274         
28275         if(this.footer){
28276             var footer = {
28277                 tag : 'a',
28278                 cls : 'small-box-footer',
28279                 href : this.fhref || '#',
28280                 html : this.footer
28281             };
28282             
28283             cfg.cn.push(footer);
28284             
28285         }
28286         
28287         return  cfg;
28288     },
28289
28290     onRender : function(ct,position){
28291         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28292
28293
28294        
28295                 
28296     },
28297
28298     setHeadline: function (value)
28299     {
28300         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28301     },
28302     
28303     setFooter: function (value, href)
28304     {
28305         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28306         
28307         if(href){
28308             this.el.select('a.small-box-footer',true).first().attr('href', href);
28309         }
28310         
28311     },
28312
28313     setContent: function (value)
28314     {
28315         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28316     },
28317
28318     initEvents: function() 
28319     {   
28320         
28321     }
28322     
28323 });
28324
28325  
28326 /*
28327  * - LGPL
28328  *
28329  * TabBox
28330  * 
28331  */
28332 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28333
28334 /**
28335  * @class Roo.bootstrap.dash.TabBox
28336  * @extends Roo.bootstrap.Component
28337  * Bootstrap TabBox class
28338  * @cfg {String} title Title of the TabBox
28339  * @cfg {String} icon Icon of the TabBox
28340  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28341  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28342  * 
28343  * @constructor
28344  * Create a new TabBox
28345  * @param {Object} config The config object
28346  */
28347
28348
28349 Roo.bootstrap.dash.TabBox = function(config){
28350     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28351     this.addEvents({
28352         // raw events
28353         /**
28354          * @event addpane
28355          * When a pane is added
28356          * @param {Roo.bootstrap.dash.TabPane} pane
28357          */
28358         "addpane" : true,
28359         /**
28360          * @event activatepane
28361          * When a pane is activated
28362          * @param {Roo.bootstrap.dash.TabPane} pane
28363          */
28364         "activatepane" : true
28365         
28366          
28367     });
28368     
28369     this.panes = [];
28370 };
28371
28372 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28373
28374     title : '',
28375     icon : false,
28376     showtabs : true,
28377     tabScrollable : false,
28378     
28379     getChildContainer : function()
28380     {
28381         return this.el.select('.tab-content', true).first();
28382     },
28383     
28384     getAutoCreate : function(){
28385         
28386         var header = {
28387             tag: 'li',
28388             cls: 'pull-left header',
28389             html: this.title,
28390             cn : []
28391         };
28392         
28393         if(this.icon){
28394             header.cn.push({
28395                 tag: 'i',
28396                 cls: 'fa ' + this.icon
28397             });
28398         }
28399         
28400         var h = {
28401             tag: 'ul',
28402             cls: 'nav nav-tabs pull-right',
28403             cn: [
28404                 header
28405             ]
28406         };
28407         
28408         if(this.tabScrollable){
28409             h = {
28410                 tag: 'div',
28411                 cls: 'tab-header',
28412                 cn: [
28413                     {
28414                         tag: 'ul',
28415                         cls: 'nav nav-tabs pull-right',
28416                         cn: [
28417                             header
28418                         ]
28419                     }
28420                 ]
28421             };
28422         }
28423         
28424         var cfg = {
28425             tag: 'div',
28426             cls: 'nav-tabs-custom',
28427             cn: [
28428                 h,
28429                 {
28430                     tag: 'div',
28431                     cls: 'tab-content no-padding',
28432                     cn: []
28433                 }
28434             ]
28435         };
28436
28437         return  cfg;
28438     },
28439     initEvents : function()
28440     {
28441         //Roo.log('add add pane handler');
28442         this.on('addpane', this.onAddPane, this);
28443     },
28444      /**
28445      * Updates the box title
28446      * @param {String} html to set the title to.
28447      */
28448     setTitle : function(value)
28449     {
28450         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28451     },
28452     onAddPane : function(pane)
28453     {
28454         this.panes.push(pane);
28455         //Roo.log('addpane');
28456         //Roo.log(pane);
28457         // tabs are rendere left to right..
28458         if(!this.showtabs){
28459             return;
28460         }
28461         
28462         var ctr = this.el.select('.nav-tabs', true).first();
28463          
28464          
28465         var existing = ctr.select('.nav-tab',true);
28466         var qty = existing.getCount();;
28467         
28468         
28469         var tab = ctr.createChild({
28470             tag : 'li',
28471             cls : 'nav-tab' + (qty ? '' : ' active'),
28472             cn : [
28473                 {
28474                     tag : 'a',
28475                     href:'#',
28476                     html : pane.title
28477                 }
28478             ]
28479         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28480         pane.tab = tab;
28481         
28482         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28483         if (!qty) {
28484             pane.el.addClass('active');
28485         }
28486         
28487                 
28488     },
28489     onTabClick : function(ev,un,ob,pane)
28490     {
28491         //Roo.log('tab - prev default');
28492         ev.preventDefault();
28493         
28494         
28495         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28496         pane.tab.addClass('active');
28497         //Roo.log(pane.title);
28498         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28499         // technically we should have a deactivate event.. but maybe add later.
28500         // and it should not de-activate the selected tab...
28501         this.fireEvent('activatepane', pane);
28502         pane.el.addClass('active');
28503         pane.fireEvent('activate');
28504         
28505         
28506     },
28507     
28508     getActivePane : function()
28509     {
28510         var r = false;
28511         Roo.each(this.panes, function(p) {
28512             if(p.el.hasClass('active')){
28513                 r = p;
28514                 return false;
28515             }
28516             
28517             return;
28518         });
28519         
28520         return r;
28521     }
28522     
28523     
28524 });
28525
28526  
28527 /*
28528  * - LGPL
28529  *
28530  * Tab pane
28531  * 
28532  */
28533 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28534 /**
28535  * @class Roo.bootstrap.TabPane
28536  * @extends Roo.bootstrap.Component
28537  * Bootstrap TabPane class
28538  * @cfg {Boolean} active (false | true) Default false
28539  * @cfg {String} title title of panel
28540
28541  * 
28542  * @constructor
28543  * Create a new TabPane
28544  * @param {Object} config The config object
28545  */
28546
28547 Roo.bootstrap.dash.TabPane = function(config){
28548     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28549     
28550     this.addEvents({
28551         // raw events
28552         /**
28553          * @event activate
28554          * When a pane is activated
28555          * @param {Roo.bootstrap.dash.TabPane} pane
28556          */
28557         "activate" : true
28558          
28559     });
28560 };
28561
28562 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28563     
28564     active : false,
28565     title : '',
28566     
28567     // the tabBox that this is attached to.
28568     tab : false,
28569      
28570     getAutoCreate : function() 
28571     {
28572         var cfg = {
28573             tag: 'div',
28574             cls: 'tab-pane'
28575         };
28576         
28577         if(this.active){
28578             cfg.cls += ' active';
28579         }
28580         
28581         return cfg;
28582     },
28583     initEvents  : function()
28584     {
28585         //Roo.log('trigger add pane handler');
28586         this.parent().fireEvent('addpane', this)
28587     },
28588     
28589      /**
28590      * Updates the tab title 
28591      * @param {String} html to set the title to.
28592      */
28593     setTitle: function(str)
28594     {
28595         if (!this.tab) {
28596             return;
28597         }
28598         this.title = str;
28599         this.tab.select('a', true).first().dom.innerHTML = str;
28600         
28601     }
28602     
28603     
28604     
28605 });
28606
28607  
28608
28609
28610  /*
28611  * - LGPL
28612  *
28613  * menu
28614  * 
28615  */
28616 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28617
28618 /**
28619  * @class Roo.bootstrap.menu.Menu
28620  * @extends Roo.bootstrap.Component
28621  * Bootstrap Menu class - container for Menu
28622  * @cfg {String} html Text of the menu
28623  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28624  * @cfg {String} icon Font awesome icon
28625  * @cfg {String} pos Menu align to (top | bottom) default bottom
28626  * 
28627  * 
28628  * @constructor
28629  * Create a new Menu
28630  * @param {Object} config The config object
28631  */
28632
28633
28634 Roo.bootstrap.menu.Menu = function(config){
28635     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28636     
28637     this.addEvents({
28638         /**
28639          * @event beforeshow
28640          * Fires before this menu is displayed
28641          * @param {Roo.bootstrap.menu.Menu} this
28642          */
28643         beforeshow : true,
28644         /**
28645          * @event beforehide
28646          * Fires before this menu is hidden
28647          * @param {Roo.bootstrap.menu.Menu} this
28648          */
28649         beforehide : true,
28650         /**
28651          * @event show
28652          * Fires after this menu is displayed
28653          * @param {Roo.bootstrap.menu.Menu} this
28654          */
28655         show : true,
28656         /**
28657          * @event hide
28658          * Fires after this menu is hidden
28659          * @param {Roo.bootstrap.menu.Menu} this
28660          */
28661         hide : true,
28662         /**
28663          * @event click
28664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28665          * @param {Roo.bootstrap.menu.Menu} this
28666          * @param {Roo.EventObject} e
28667          */
28668         click : true
28669     });
28670     
28671 };
28672
28673 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28674     
28675     submenu : false,
28676     html : '',
28677     weight : 'default',
28678     icon : false,
28679     pos : 'bottom',
28680     
28681     
28682     getChildContainer : function() {
28683         if(this.isSubMenu){
28684             return this.el;
28685         }
28686         
28687         return this.el.select('ul.dropdown-menu', true).first();  
28688     },
28689     
28690     getAutoCreate : function()
28691     {
28692         var text = [
28693             {
28694                 tag : 'span',
28695                 cls : 'roo-menu-text',
28696                 html : this.html
28697             }
28698         ];
28699         
28700         if(this.icon){
28701             text.unshift({
28702                 tag : 'i',
28703                 cls : 'fa ' + this.icon
28704             })
28705         }
28706         
28707         
28708         var cfg = {
28709             tag : 'div',
28710             cls : 'btn-group',
28711             cn : [
28712                 {
28713                     tag : 'button',
28714                     cls : 'dropdown-button btn btn-' + this.weight,
28715                     cn : text
28716                 },
28717                 {
28718                     tag : 'button',
28719                     cls : 'dropdown-toggle btn btn-' + this.weight,
28720                     cn : [
28721                         {
28722                             tag : 'span',
28723                             cls : 'caret'
28724                         }
28725                     ]
28726                 },
28727                 {
28728                     tag : 'ul',
28729                     cls : 'dropdown-menu'
28730                 }
28731             ]
28732             
28733         };
28734         
28735         if(this.pos == 'top'){
28736             cfg.cls += ' dropup';
28737         }
28738         
28739         if(this.isSubMenu){
28740             cfg = {
28741                 tag : 'ul',
28742                 cls : 'dropdown-menu'
28743             }
28744         }
28745         
28746         return cfg;
28747     },
28748     
28749     onRender : function(ct, position)
28750     {
28751         this.isSubMenu = ct.hasClass('dropdown-submenu');
28752         
28753         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28754     },
28755     
28756     initEvents : function() 
28757     {
28758         if(this.isSubMenu){
28759             return;
28760         }
28761         
28762         this.hidden = true;
28763         
28764         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28765         this.triggerEl.on('click', this.onTriggerPress, this);
28766         
28767         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28768         this.buttonEl.on('click', this.onClick, this);
28769         
28770     },
28771     
28772     list : function()
28773     {
28774         if(this.isSubMenu){
28775             return this.el;
28776         }
28777         
28778         return this.el.select('ul.dropdown-menu', true).first();
28779     },
28780     
28781     onClick : function(e)
28782     {
28783         this.fireEvent("click", this, e);
28784     },
28785     
28786     onTriggerPress  : function(e)
28787     {   
28788         if (this.isVisible()) {
28789             this.hide();
28790         } else {
28791             this.show();
28792         }
28793     },
28794     
28795     isVisible : function(){
28796         return !this.hidden;
28797     },
28798     
28799     show : function()
28800     {
28801         this.fireEvent("beforeshow", this);
28802         
28803         this.hidden = false;
28804         this.el.addClass('open');
28805         
28806         Roo.get(document).on("mouseup", this.onMouseUp, this);
28807         
28808         this.fireEvent("show", this);
28809         
28810         
28811     },
28812     
28813     hide : function()
28814     {
28815         this.fireEvent("beforehide", this);
28816         
28817         this.hidden = true;
28818         this.el.removeClass('open');
28819         
28820         Roo.get(document).un("mouseup", this.onMouseUp);
28821         
28822         this.fireEvent("hide", this);
28823     },
28824     
28825     onMouseUp : function()
28826     {
28827         this.hide();
28828     }
28829     
28830 });
28831
28832  
28833  /*
28834  * - LGPL
28835  *
28836  * menu item
28837  * 
28838  */
28839 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28840
28841 /**
28842  * @class Roo.bootstrap.menu.Item
28843  * @extends Roo.bootstrap.Component
28844  * Bootstrap MenuItem class
28845  * @cfg {Boolean} submenu (true | false) default false
28846  * @cfg {String} html text of the item
28847  * @cfg {String} href the link
28848  * @cfg {Boolean} disable (true | false) default false
28849  * @cfg {Boolean} preventDefault (true | false) default true
28850  * @cfg {String} icon Font awesome icon
28851  * @cfg {String} pos Submenu align to (left | right) default right 
28852  * 
28853  * 
28854  * @constructor
28855  * Create a new Item
28856  * @param {Object} config The config object
28857  */
28858
28859
28860 Roo.bootstrap.menu.Item = function(config){
28861     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28862     this.addEvents({
28863         /**
28864          * @event mouseover
28865          * Fires when the mouse is hovering over this menu
28866          * @param {Roo.bootstrap.menu.Item} this
28867          * @param {Roo.EventObject} e
28868          */
28869         mouseover : true,
28870         /**
28871          * @event mouseout
28872          * Fires when the mouse exits this menu
28873          * @param {Roo.bootstrap.menu.Item} this
28874          * @param {Roo.EventObject} e
28875          */
28876         mouseout : true,
28877         // raw events
28878         /**
28879          * @event click
28880          * The raw click event for the entire grid.
28881          * @param {Roo.EventObject} e
28882          */
28883         click : true
28884     });
28885 };
28886
28887 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28888     
28889     submenu : false,
28890     href : '',
28891     html : '',
28892     preventDefault: true,
28893     disable : false,
28894     icon : false,
28895     pos : 'right',
28896     
28897     getAutoCreate : function()
28898     {
28899         var text = [
28900             {
28901                 tag : 'span',
28902                 cls : 'roo-menu-item-text',
28903                 html : this.html
28904             }
28905         ];
28906         
28907         if(this.icon){
28908             text.unshift({
28909                 tag : 'i',
28910                 cls : 'fa ' + this.icon
28911             })
28912         }
28913         
28914         var cfg = {
28915             tag : 'li',
28916             cn : [
28917                 {
28918                     tag : 'a',
28919                     href : this.href || '#',
28920                     cn : text
28921                 }
28922             ]
28923         };
28924         
28925         if(this.disable){
28926             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28927         }
28928         
28929         if(this.submenu){
28930             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28931             
28932             if(this.pos == 'left'){
28933                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28934             }
28935         }
28936         
28937         return cfg;
28938     },
28939     
28940     initEvents : function() 
28941     {
28942         this.el.on('mouseover', this.onMouseOver, this);
28943         this.el.on('mouseout', this.onMouseOut, this);
28944         
28945         this.el.select('a', true).first().on('click', this.onClick, this);
28946         
28947     },
28948     
28949     onClick : function(e)
28950     {
28951         if(this.preventDefault){
28952             e.preventDefault();
28953         }
28954         
28955         this.fireEvent("click", this, e);
28956     },
28957     
28958     onMouseOver : function(e)
28959     {
28960         if(this.submenu && this.pos == 'left'){
28961             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28962         }
28963         
28964         this.fireEvent("mouseover", this, e);
28965     },
28966     
28967     onMouseOut : function(e)
28968     {
28969         this.fireEvent("mouseout", this, e);
28970     }
28971 });
28972
28973  
28974
28975  /*
28976  * - LGPL
28977  *
28978  * menu separator
28979  * 
28980  */
28981 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28982
28983 /**
28984  * @class Roo.bootstrap.menu.Separator
28985  * @extends Roo.bootstrap.Component
28986  * Bootstrap Separator class
28987  * 
28988  * @constructor
28989  * Create a new Separator
28990  * @param {Object} config The config object
28991  */
28992
28993
28994 Roo.bootstrap.menu.Separator = function(config){
28995     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28996 };
28997
28998 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28999     
29000     getAutoCreate : function(){
29001         var cfg = {
29002             tag : 'li',
29003             cls: 'dropdown-divider divider'
29004         };
29005         
29006         return cfg;
29007     }
29008    
29009 });
29010
29011  
29012
29013  /*
29014  * - LGPL
29015  *
29016  * Tooltip
29017  * 
29018  */
29019
29020 /**
29021  * @class Roo.bootstrap.Tooltip
29022  * Bootstrap Tooltip class
29023  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29024  * to determine which dom element triggers the tooltip.
29025  * 
29026  * It needs to add support for additional attributes like tooltip-position
29027  * 
29028  * @constructor
29029  * Create a new Toolti
29030  * @param {Object} config The config object
29031  */
29032
29033 Roo.bootstrap.Tooltip = function(config){
29034     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29035     
29036     this.alignment = Roo.bootstrap.Tooltip.alignment;
29037     
29038     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29039         this.alignment = config.alignment;
29040     }
29041     
29042 };
29043
29044 Roo.apply(Roo.bootstrap.Tooltip, {
29045     /**
29046      * @function init initialize tooltip monitoring.
29047      * @static
29048      */
29049     currentEl : false,
29050     currentTip : false,
29051     currentRegion : false,
29052     
29053     //  init : delay?
29054     
29055     init : function()
29056     {
29057         Roo.get(document).on('mouseover', this.enter ,this);
29058         Roo.get(document).on('mouseout', this.leave, this);
29059          
29060         
29061         this.currentTip = new Roo.bootstrap.Tooltip();
29062     },
29063     
29064     enter : function(ev)
29065     {
29066         var dom = ev.getTarget();
29067         
29068         //Roo.log(['enter',dom]);
29069         var el = Roo.fly(dom);
29070         if (this.currentEl) {
29071             //Roo.log(dom);
29072             //Roo.log(this.currentEl);
29073             //Roo.log(this.currentEl.contains(dom));
29074             if (this.currentEl == el) {
29075                 return;
29076             }
29077             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29078                 return;
29079             }
29080
29081         }
29082         
29083         if (this.currentTip.el) {
29084             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29085         }    
29086         //Roo.log(ev);
29087         
29088         if(!el || el.dom == document){
29089             return;
29090         }
29091         
29092         var bindEl = el; 
29093         var pel = false;
29094         if (!el.attr('tooltip')) {
29095             pel = el.findParent("[tooltip]");
29096             if (pel) {
29097                 bindEl = Roo.get(pel);
29098             }
29099         }
29100         
29101        
29102         
29103         // you can not look for children, as if el is the body.. then everythign is the child..
29104         if (!pel && !el.attr('tooltip')) { //
29105             if (!el.select("[tooltip]").elements.length) {
29106                 return;
29107             }
29108             // is the mouse over this child...?
29109             bindEl = el.select("[tooltip]").first();
29110             var xy = ev.getXY();
29111             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29112                 //Roo.log("not in region.");
29113                 return;
29114             }
29115             //Roo.log("child element over..");
29116             
29117         }
29118         this.currentEl = el;
29119         this.currentTip.bind(bindEl);
29120         this.currentRegion = Roo.lib.Region.getRegion(dom);
29121         this.currentTip.enter();
29122         
29123     },
29124     leave : function(ev)
29125     {
29126         var dom = ev.getTarget();
29127         //Roo.log(['leave',dom]);
29128         if (!this.currentEl) {
29129             return;
29130         }
29131         
29132         
29133         if (dom != this.currentEl.dom) {
29134             return;
29135         }
29136         var xy = ev.getXY();
29137         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29138             return;
29139         }
29140         // only activate leave if mouse cursor is outside... bounding box..
29141         
29142         
29143         
29144         
29145         if (this.currentTip) {
29146             this.currentTip.leave();
29147         }
29148         //Roo.log('clear currentEl');
29149         this.currentEl = false;
29150         
29151         
29152     },
29153     alignment : {
29154         'left' : ['r-l', [-2,0], 'right'],
29155         'right' : ['l-r', [2,0], 'left'],
29156         'bottom' : ['t-b', [0,2], 'top'],
29157         'top' : [ 'b-t', [0,-2], 'bottom']
29158     }
29159     
29160 });
29161
29162
29163 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29164     
29165     
29166     bindEl : false,
29167     
29168     delay : null, // can be { show : 300 , hide: 500}
29169     
29170     timeout : null,
29171     
29172     hoverState : null, //???
29173     
29174     placement : 'bottom', 
29175     
29176     alignment : false,
29177     
29178     getAutoCreate : function(){
29179     
29180         var cfg = {
29181            cls : 'tooltip',   
29182            role : 'tooltip',
29183            cn : [
29184                 {
29185                     cls : 'tooltip-arrow arrow'
29186                 },
29187                 {
29188                     cls : 'tooltip-inner'
29189                 }
29190            ]
29191         };
29192         
29193         return cfg;
29194     },
29195     bind : function(el)
29196     {
29197         this.bindEl = el;
29198     },
29199     
29200     initEvents : function()
29201     {
29202         this.arrowEl = this.el.select('.arrow', true).first();
29203         this.innerEl = this.el.select('.tooltip-inner', true).first();
29204     },
29205     
29206     enter : function () {
29207        
29208         if (this.timeout != null) {
29209             clearTimeout(this.timeout);
29210         }
29211         
29212         this.hoverState = 'in';
29213          //Roo.log("enter - show");
29214         if (!this.delay || !this.delay.show) {
29215             this.show();
29216             return;
29217         }
29218         var _t = this;
29219         this.timeout = setTimeout(function () {
29220             if (_t.hoverState == 'in') {
29221                 _t.show();
29222             }
29223         }, this.delay.show);
29224     },
29225     leave : function()
29226     {
29227         clearTimeout(this.timeout);
29228     
29229         this.hoverState = 'out';
29230          if (!this.delay || !this.delay.hide) {
29231             this.hide();
29232             return;
29233         }
29234        
29235         var _t = this;
29236         this.timeout = setTimeout(function () {
29237             //Roo.log("leave - timeout");
29238             
29239             if (_t.hoverState == 'out') {
29240                 _t.hide();
29241                 Roo.bootstrap.Tooltip.currentEl = false;
29242             }
29243         }, delay);
29244     },
29245     
29246     show : function (msg)
29247     {
29248         if (!this.el) {
29249             this.render(document.body);
29250         }
29251         // set content.
29252         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29253         
29254         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29255         
29256         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29257         
29258         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29259                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29260         
29261         var placement = typeof this.placement == 'function' ?
29262             this.placement.call(this, this.el, on_el) :
29263             this.placement;
29264             
29265         var autoToken = /\s?auto?\s?/i;
29266         var autoPlace = autoToken.test(placement);
29267         if (autoPlace) {
29268             placement = placement.replace(autoToken, '') || 'top';
29269         }
29270         
29271         //this.el.detach()
29272         //this.el.setXY([0,0]);
29273         this.el.show();
29274         //this.el.dom.style.display='block';
29275         
29276         //this.el.appendTo(on_el);
29277         
29278         var p = this.getPosition();
29279         var box = this.el.getBox();
29280         
29281         if (autoPlace) {
29282             // fixme..
29283         }
29284         
29285         var align = this.alignment[placement];
29286         
29287         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29288         
29289         if(placement == 'top' || placement == 'bottom'){
29290             if(xy[0] < 0){
29291                 placement = 'right';
29292             }
29293             
29294             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29295                 placement = 'left';
29296             }
29297             
29298             var scroll = Roo.select('body', true).first().getScroll();
29299             
29300             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29301                 placement = 'top';
29302             }
29303             
29304             align = this.alignment[placement];
29305             
29306             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29307             
29308         }
29309         
29310         var elems = document.getElementsByTagName('div');
29311         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29312         for (var i = 0; i < elems.length; i++) {
29313           var zindex = Number.parseInt(
29314                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29315                 10
29316           );
29317           if (zindex > highest) {
29318             highest = zindex;
29319           }
29320         }
29321         
29322         
29323         
29324         this.el.dom.style.zIndex = highest;
29325         
29326         this.el.alignTo(this.bindEl, align[0],align[1]);
29327         //var arrow = this.el.select('.arrow',true).first();
29328         //arrow.set(align[2], 
29329         
29330         this.el.addClass(placement);
29331         this.el.addClass("bs-tooltip-"+ placement);
29332         
29333         this.el.addClass('in fade show');
29334         
29335         this.hoverState = null;
29336         
29337         if (this.el.hasClass('fade')) {
29338             // fade it?
29339         }
29340         
29341         
29342         
29343         
29344         
29345     },
29346     hide : function()
29347     {
29348          
29349         if (!this.el) {
29350             return;
29351         }
29352         //this.el.setXY([0,0]);
29353         this.el.removeClass(['show', 'in']);
29354         //this.el.hide();
29355         
29356     }
29357     
29358 });
29359  
29360
29361  /*
29362  * - LGPL
29363  *
29364  * Location Picker
29365  * 
29366  */
29367
29368 /**
29369  * @class Roo.bootstrap.LocationPicker
29370  * @extends Roo.bootstrap.Component
29371  * Bootstrap LocationPicker class
29372  * @cfg {Number} latitude Position when init default 0
29373  * @cfg {Number} longitude Position when init default 0
29374  * @cfg {Number} zoom default 15
29375  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29376  * @cfg {Boolean} mapTypeControl default false
29377  * @cfg {Boolean} disableDoubleClickZoom default false
29378  * @cfg {Boolean} scrollwheel default true
29379  * @cfg {Boolean} streetViewControl default false
29380  * @cfg {Number} radius default 0
29381  * @cfg {String} locationName
29382  * @cfg {Boolean} draggable default true
29383  * @cfg {Boolean} enableAutocomplete default false
29384  * @cfg {Boolean} enableReverseGeocode default true
29385  * @cfg {String} markerTitle
29386  * 
29387  * @constructor
29388  * Create a new LocationPicker
29389  * @param {Object} config The config object
29390  */
29391
29392
29393 Roo.bootstrap.LocationPicker = function(config){
29394     
29395     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29396     
29397     this.addEvents({
29398         /**
29399          * @event initial
29400          * Fires when the picker initialized.
29401          * @param {Roo.bootstrap.LocationPicker} this
29402          * @param {Google Location} location
29403          */
29404         initial : true,
29405         /**
29406          * @event positionchanged
29407          * Fires when the picker position changed.
29408          * @param {Roo.bootstrap.LocationPicker} this
29409          * @param {Google Location} location
29410          */
29411         positionchanged : true,
29412         /**
29413          * @event resize
29414          * Fires when the map resize.
29415          * @param {Roo.bootstrap.LocationPicker} this
29416          */
29417         resize : true,
29418         /**
29419          * @event show
29420          * Fires when the map show.
29421          * @param {Roo.bootstrap.LocationPicker} this
29422          */
29423         show : true,
29424         /**
29425          * @event hide
29426          * Fires when the map hide.
29427          * @param {Roo.bootstrap.LocationPicker} this
29428          */
29429         hide : true,
29430         /**
29431          * @event mapClick
29432          * Fires when click the map.
29433          * @param {Roo.bootstrap.LocationPicker} this
29434          * @param {Map event} e
29435          */
29436         mapClick : true,
29437         /**
29438          * @event mapRightClick
29439          * Fires when right click the map.
29440          * @param {Roo.bootstrap.LocationPicker} this
29441          * @param {Map event} e
29442          */
29443         mapRightClick : true,
29444         /**
29445          * @event markerClick
29446          * Fires when click the marker.
29447          * @param {Roo.bootstrap.LocationPicker} this
29448          * @param {Map event} e
29449          */
29450         markerClick : true,
29451         /**
29452          * @event markerRightClick
29453          * Fires when right click the marker.
29454          * @param {Roo.bootstrap.LocationPicker} this
29455          * @param {Map event} e
29456          */
29457         markerRightClick : true,
29458         /**
29459          * @event OverlayViewDraw
29460          * Fires when OverlayView Draw
29461          * @param {Roo.bootstrap.LocationPicker} this
29462          */
29463         OverlayViewDraw : true,
29464         /**
29465          * @event OverlayViewOnAdd
29466          * Fires when OverlayView Draw
29467          * @param {Roo.bootstrap.LocationPicker} this
29468          */
29469         OverlayViewOnAdd : true,
29470         /**
29471          * @event OverlayViewOnRemove
29472          * Fires when OverlayView Draw
29473          * @param {Roo.bootstrap.LocationPicker} this
29474          */
29475         OverlayViewOnRemove : true,
29476         /**
29477          * @event OverlayViewShow
29478          * Fires when OverlayView Draw
29479          * @param {Roo.bootstrap.LocationPicker} this
29480          * @param {Pixel} cpx
29481          */
29482         OverlayViewShow : true,
29483         /**
29484          * @event OverlayViewHide
29485          * Fires when OverlayView Draw
29486          * @param {Roo.bootstrap.LocationPicker} this
29487          */
29488         OverlayViewHide : true,
29489         /**
29490          * @event loadexception
29491          * Fires when load google lib failed.
29492          * @param {Roo.bootstrap.LocationPicker} this
29493          */
29494         loadexception : true
29495     });
29496         
29497 };
29498
29499 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29500     
29501     gMapContext: false,
29502     
29503     latitude: 0,
29504     longitude: 0,
29505     zoom: 15,
29506     mapTypeId: false,
29507     mapTypeControl: false,
29508     disableDoubleClickZoom: false,
29509     scrollwheel: true,
29510     streetViewControl: false,
29511     radius: 0,
29512     locationName: '',
29513     draggable: true,
29514     enableAutocomplete: false,
29515     enableReverseGeocode: true,
29516     markerTitle: '',
29517     
29518     getAutoCreate: function()
29519     {
29520
29521         var cfg = {
29522             tag: 'div',
29523             cls: 'roo-location-picker'
29524         };
29525         
29526         return cfg
29527     },
29528     
29529     initEvents: function(ct, position)
29530     {       
29531         if(!this.el.getWidth() || this.isApplied()){
29532             return;
29533         }
29534         
29535         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29536         
29537         this.initial();
29538     },
29539     
29540     initial: function()
29541     {
29542         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29543             this.fireEvent('loadexception', this);
29544             return;
29545         }
29546         
29547         if(!this.mapTypeId){
29548             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29549         }
29550         
29551         this.gMapContext = this.GMapContext();
29552         
29553         this.initOverlayView();
29554         
29555         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29556         
29557         var _this = this;
29558                 
29559         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29560             _this.setPosition(_this.gMapContext.marker.position);
29561         });
29562         
29563         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29564             _this.fireEvent('mapClick', this, event);
29565             
29566         });
29567
29568         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29569             _this.fireEvent('mapRightClick', this, event);
29570             
29571         });
29572         
29573         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29574             _this.fireEvent('markerClick', this, event);
29575             
29576         });
29577
29578         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29579             _this.fireEvent('markerRightClick', this, event);
29580             
29581         });
29582         
29583         this.setPosition(this.gMapContext.location);
29584         
29585         this.fireEvent('initial', this, this.gMapContext.location);
29586     },
29587     
29588     initOverlayView: function()
29589     {
29590         var _this = this;
29591         
29592         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29593             
29594             draw: function()
29595             {
29596                 _this.fireEvent('OverlayViewDraw', _this);
29597             },
29598             
29599             onAdd: function()
29600             {
29601                 _this.fireEvent('OverlayViewOnAdd', _this);
29602             },
29603             
29604             onRemove: function()
29605             {
29606                 _this.fireEvent('OverlayViewOnRemove', _this);
29607             },
29608             
29609             show: function(cpx)
29610             {
29611                 _this.fireEvent('OverlayViewShow', _this, cpx);
29612             },
29613             
29614             hide: function()
29615             {
29616                 _this.fireEvent('OverlayViewHide', _this);
29617             }
29618             
29619         });
29620     },
29621     
29622     fromLatLngToContainerPixel: function(event)
29623     {
29624         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29625     },
29626     
29627     isApplied: function() 
29628     {
29629         return this.getGmapContext() == false ? false : true;
29630     },
29631     
29632     getGmapContext: function() 
29633     {
29634         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29635     },
29636     
29637     GMapContext: function() 
29638     {
29639         var position = new google.maps.LatLng(this.latitude, this.longitude);
29640         
29641         var _map = new google.maps.Map(this.el.dom, {
29642             center: position,
29643             zoom: this.zoom,
29644             mapTypeId: this.mapTypeId,
29645             mapTypeControl: this.mapTypeControl,
29646             disableDoubleClickZoom: this.disableDoubleClickZoom,
29647             scrollwheel: this.scrollwheel,
29648             streetViewControl: this.streetViewControl,
29649             locationName: this.locationName,
29650             draggable: this.draggable,
29651             enableAutocomplete: this.enableAutocomplete,
29652             enableReverseGeocode: this.enableReverseGeocode
29653         });
29654         
29655         var _marker = new google.maps.Marker({
29656             position: position,
29657             map: _map,
29658             title: this.markerTitle,
29659             draggable: this.draggable
29660         });
29661         
29662         return {
29663             map: _map,
29664             marker: _marker,
29665             circle: null,
29666             location: position,
29667             radius: this.radius,
29668             locationName: this.locationName,
29669             addressComponents: {
29670                 formatted_address: null,
29671                 addressLine1: null,
29672                 addressLine2: null,
29673                 streetName: null,
29674                 streetNumber: null,
29675                 city: null,
29676                 district: null,
29677                 state: null,
29678                 stateOrProvince: null
29679             },
29680             settings: this,
29681             domContainer: this.el.dom,
29682             geodecoder: new google.maps.Geocoder()
29683         };
29684     },
29685     
29686     drawCircle: function(center, radius, options) 
29687     {
29688         if (this.gMapContext.circle != null) {
29689             this.gMapContext.circle.setMap(null);
29690         }
29691         if (radius > 0) {
29692             radius *= 1;
29693             options = Roo.apply({}, options, {
29694                 strokeColor: "#0000FF",
29695                 strokeOpacity: .35,
29696                 strokeWeight: 2,
29697                 fillColor: "#0000FF",
29698                 fillOpacity: .2
29699             });
29700             
29701             options.map = this.gMapContext.map;
29702             options.radius = radius;
29703             options.center = center;
29704             this.gMapContext.circle = new google.maps.Circle(options);
29705             return this.gMapContext.circle;
29706         }
29707         
29708         return null;
29709     },
29710     
29711     setPosition: function(location) 
29712     {
29713         this.gMapContext.location = location;
29714         this.gMapContext.marker.setPosition(location);
29715         this.gMapContext.map.panTo(location);
29716         this.drawCircle(location, this.gMapContext.radius, {});
29717         
29718         var _this = this;
29719         
29720         if (this.gMapContext.settings.enableReverseGeocode) {
29721             this.gMapContext.geodecoder.geocode({
29722                 latLng: this.gMapContext.location
29723             }, function(results, status) {
29724                 
29725                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29726                     _this.gMapContext.locationName = results[0].formatted_address;
29727                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29728                     
29729                     _this.fireEvent('positionchanged', this, location);
29730                 }
29731             });
29732             
29733             return;
29734         }
29735         
29736         this.fireEvent('positionchanged', this, location);
29737     },
29738     
29739     resize: function()
29740     {
29741         google.maps.event.trigger(this.gMapContext.map, "resize");
29742         
29743         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29744         
29745         this.fireEvent('resize', this);
29746     },
29747     
29748     setPositionByLatLng: function(latitude, longitude)
29749     {
29750         this.setPosition(new google.maps.LatLng(latitude, longitude));
29751     },
29752     
29753     getCurrentPosition: function() 
29754     {
29755         return {
29756             latitude: this.gMapContext.location.lat(),
29757             longitude: this.gMapContext.location.lng()
29758         };
29759     },
29760     
29761     getAddressName: function() 
29762     {
29763         return this.gMapContext.locationName;
29764     },
29765     
29766     getAddressComponents: function() 
29767     {
29768         return this.gMapContext.addressComponents;
29769     },
29770     
29771     address_component_from_google_geocode: function(address_components) 
29772     {
29773         var result = {};
29774         
29775         for (var i = 0; i < address_components.length; i++) {
29776             var component = address_components[i];
29777             if (component.types.indexOf("postal_code") >= 0) {
29778                 result.postalCode = component.short_name;
29779             } else if (component.types.indexOf("street_number") >= 0) {
29780                 result.streetNumber = component.short_name;
29781             } else if (component.types.indexOf("route") >= 0) {
29782                 result.streetName = component.short_name;
29783             } else if (component.types.indexOf("neighborhood") >= 0) {
29784                 result.city = component.short_name;
29785             } else if (component.types.indexOf("locality") >= 0) {
29786                 result.city = component.short_name;
29787             } else if (component.types.indexOf("sublocality") >= 0) {
29788                 result.district = component.short_name;
29789             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29790                 result.stateOrProvince = component.short_name;
29791             } else if (component.types.indexOf("country") >= 0) {
29792                 result.country = component.short_name;
29793             }
29794         }
29795         
29796         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29797         result.addressLine2 = "";
29798         return result;
29799     },
29800     
29801     setZoomLevel: function(zoom)
29802     {
29803         this.gMapContext.map.setZoom(zoom);
29804     },
29805     
29806     show: function()
29807     {
29808         if(!this.el){
29809             return;
29810         }
29811         
29812         this.el.show();
29813         
29814         this.resize();
29815         
29816         this.fireEvent('show', this);
29817     },
29818     
29819     hide: function()
29820     {
29821         if(!this.el){
29822             return;
29823         }
29824         
29825         this.el.hide();
29826         
29827         this.fireEvent('hide', this);
29828     }
29829     
29830 });
29831
29832 Roo.apply(Roo.bootstrap.LocationPicker, {
29833     
29834     OverlayView : function(map, options)
29835     {
29836         options = options || {};
29837         
29838         this.setMap(map);
29839     }
29840     
29841     
29842 });/**
29843  * @class Roo.bootstrap.Alert
29844  * @extends Roo.bootstrap.Component
29845  * Bootstrap Alert class - shows an alert area box
29846  * eg
29847  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29848   Enter a valid email address
29849 </div>
29850  * @licence LGPL
29851  * @cfg {String} title The title of alert
29852  * @cfg {String} html The content of alert
29853  * @cfg {String} weight (  success | info | warning | danger )
29854  * @cfg {String} fa font-awesomeicon
29855  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29856  * @cfg {Boolean} close true to show a x closer
29857  * 
29858  * 
29859  * @constructor
29860  * Create a new alert
29861  * @param {Object} config The config object
29862  */
29863
29864
29865 Roo.bootstrap.Alert = function(config){
29866     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29867     
29868 };
29869
29870 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29871     
29872     title: '',
29873     html: '',
29874     weight: false,
29875     fa: false,
29876     faicon: false, // BC
29877     close : false,
29878     
29879     
29880     getAutoCreate : function()
29881     {
29882         
29883         var cfg = {
29884             tag : 'div',
29885             cls : 'alert',
29886             cn : [
29887                 {
29888                     tag: 'button',
29889                     type :  "button",
29890                     cls: "close",
29891                     html : '×',
29892                     style : this.close ? '' : 'display:none'
29893                 },
29894                 {
29895                     tag : 'i',
29896                     cls : 'roo-alert-icon'
29897                     
29898                 },
29899                 {
29900                     tag : 'b',
29901                     cls : 'roo-alert-title',
29902                     html : this.title
29903                 },
29904                 {
29905                     tag : 'span',
29906                     cls : 'roo-alert-text',
29907                     html : this.html
29908                 }
29909             ]
29910         };
29911         
29912         if(this.faicon){
29913             cfg.cn[0].cls += ' fa ' + this.faicon;
29914         }
29915         if(this.fa){
29916             cfg.cn[0].cls += ' fa ' + this.fa;
29917         }
29918         
29919         if(this.weight){
29920             cfg.cls += ' alert-' + this.weight;
29921         }
29922         
29923         return cfg;
29924     },
29925     
29926     initEvents: function() 
29927     {
29928         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29929         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29930         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29931         if (this.seconds > 0) {
29932             this.hide.defer(this.seconds, this);
29933         }
29934     },
29935     
29936     setTitle : function(str)
29937     {
29938         this.titleEl.dom.innerHTML = str;
29939     },
29940     
29941     setText : function(str)
29942     {
29943         this.titleEl.dom.innerHTML = str;
29944     },
29945     
29946     setWeight : function(weight)
29947     {
29948         if(this.weight){
29949             this.el.removeClass('alert-' + this.weight);
29950         }
29951         
29952         this.weight = weight;
29953         
29954         this.el.addClass('alert-' + this.weight);
29955     },
29956     
29957     setIcon : function(icon)
29958     {
29959         if(this.faicon){
29960             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29961         }
29962         
29963         this.faicon = icon;
29964         
29965         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29966     },
29967     
29968     hide: function() 
29969     {
29970         this.el.hide();   
29971     },
29972     
29973     show: function() 
29974     {  
29975         this.el.show();   
29976     }
29977     
29978 });
29979
29980  
29981 /*
29982 * Licence: LGPL
29983 */
29984
29985 /**
29986  * @class Roo.bootstrap.UploadCropbox
29987  * @extends Roo.bootstrap.Component
29988  * Bootstrap UploadCropbox class
29989  * @cfg {String} emptyText show when image has been loaded
29990  * @cfg {String} rotateNotify show when image too small to rotate
29991  * @cfg {Number} errorTimeout default 3000
29992  * @cfg {Number} minWidth default 300
29993  * @cfg {Number} minHeight default 300
29994  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29995  * @cfg {Boolean} isDocument (true|false) default false
29996  * @cfg {String} url action url
29997  * @cfg {String} paramName default 'imageUpload'
29998  * @cfg {String} method default POST
29999  * @cfg {Boolean} loadMask (true|false) default true
30000  * @cfg {Boolean} loadingText default 'Loading...'
30001  * 
30002  * @constructor
30003  * Create a new UploadCropbox
30004  * @param {Object} config The config object
30005  */
30006
30007 Roo.bootstrap.UploadCropbox = function(config){
30008     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30009     
30010     this.addEvents({
30011         /**
30012          * @event beforeselectfile
30013          * Fire before select file
30014          * @param {Roo.bootstrap.UploadCropbox} this
30015          */
30016         "beforeselectfile" : true,
30017         /**
30018          * @event initial
30019          * Fire after initEvent
30020          * @param {Roo.bootstrap.UploadCropbox} this
30021          */
30022         "initial" : true,
30023         /**
30024          * @event crop
30025          * Fire after initEvent
30026          * @param {Roo.bootstrap.UploadCropbox} this
30027          * @param {String} data
30028          */
30029         "crop" : true,
30030         /**
30031          * @event prepare
30032          * Fire when preparing the file data
30033          * @param {Roo.bootstrap.UploadCropbox} this
30034          * @param {Object} file
30035          */
30036         "prepare" : true,
30037         /**
30038          * @event exception
30039          * Fire when get exception
30040          * @param {Roo.bootstrap.UploadCropbox} this
30041          * @param {XMLHttpRequest} xhr
30042          */
30043         "exception" : true,
30044         /**
30045          * @event beforeloadcanvas
30046          * Fire before load the canvas
30047          * @param {Roo.bootstrap.UploadCropbox} this
30048          * @param {String} src
30049          */
30050         "beforeloadcanvas" : true,
30051         /**
30052          * @event trash
30053          * Fire when trash image
30054          * @param {Roo.bootstrap.UploadCropbox} this
30055          */
30056         "trash" : true,
30057         /**
30058          * @event download
30059          * Fire when download the image
30060          * @param {Roo.bootstrap.UploadCropbox} this
30061          */
30062         "download" : true,
30063         /**
30064          * @event footerbuttonclick
30065          * Fire when footerbuttonclick
30066          * @param {Roo.bootstrap.UploadCropbox} this
30067          * @param {String} type
30068          */
30069         "footerbuttonclick" : true,
30070         /**
30071          * @event resize
30072          * Fire when resize
30073          * @param {Roo.bootstrap.UploadCropbox} this
30074          */
30075         "resize" : true,
30076         /**
30077          * @event rotate
30078          * Fire when rotate the image
30079          * @param {Roo.bootstrap.UploadCropbox} this
30080          * @param {String} pos
30081          */
30082         "rotate" : true,
30083         /**
30084          * @event inspect
30085          * Fire when inspect the file
30086          * @param {Roo.bootstrap.UploadCropbox} this
30087          * @param {Object} file
30088          */
30089         "inspect" : true,
30090         /**
30091          * @event upload
30092          * Fire when xhr upload the file
30093          * @param {Roo.bootstrap.UploadCropbox} this
30094          * @param {Object} data
30095          */
30096         "upload" : true,
30097         /**
30098          * @event arrange
30099          * Fire when arrange the file data
30100          * @param {Roo.bootstrap.UploadCropbox} this
30101          * @param {Object} formData
30102          */
30103         "arrange" : true
30104     });
30105     
30106     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30107 };
30108
30109 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30110     
30111     emptyText : 'Click to upload image',
30112     rotateNotify : 'Image is too small to rotate',
30113     errorTimeout : 3000,
30114     scale : 0,
30115     baseScale : 1,
30116     rotate : 0,
30117     dragable : false,
30118     pinching : false,
30119     mouseX : 0,
30120     mouseY : 0,
30121     cropData : false,
30122     minWidth : 300,
30123     minHeight : 300,
30124     file : false,
30125     exif : {},
30126     baseRotate : 1,
30127     cropType : 'image/jpeg',
30128     buttons : false,
30129     canvasLoaded : false,
30130     isDocument : false,
30131     method : 'POST',
30132     paramName : 'imageUpload',
30133     loadMask : true,
30134     loadingText : 'Loading...',
30135     maskEl : false,
30136     
30137     getAutoCreate : function()
30138     {
30139         var cfg = {
30140             tag : 'div',
30141             cls : 'roo-upload-cropbox',
30142             cn : [
30143                 {
30144                     tag : 'input',
30145                     cls : 'roo-upload-cropbox-selector',
30146                     type : 'file'
30147                 },
30148                 {
30149                     tag : 'div',
30150                     cls : 'roo-upload-cropbox-body',
30151                     style : 'cursor:pointer',
30152                     cn : [
30153                         {
30154                             tag : 'div',
30155                             cls : 'roo-upload-cropbox-preview'
30156                         },
30157                         {
30158                             tag : 'div',
30159                             cls : 'roo-upload-cropbox-thumb'
30160                         },
30161                         {
30162                             tag : 'div',
30163                             cls : 'roo-upload-cropbox-empty-notify',
30164                             html : this.emptyText
30165                         },
30166                         {
30167                             tag : 'div',
30168                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30169                             html : this.rotateNotify
30170                         }
30171                     ]
30172                 },
30173                 {
30174                     tag : 'div',
30175                     cls : 'roo-upload-cropbox-footer',
30176                     cn : {
30177                         tag : 'div',
30178                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30179                         cn : []
30180                     }
30181                 }
30182             ]
30183         };
30184         
30185         return cfg;
30186     },
30187     
30188     onRender : function(ct, position)
30189     {
30190         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30191         
30192         if (this.buttons.length) {
30193             
30194             Roo.each(this.buttons, function(bb) {
30195                 
30196                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30197                 
30198                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30199                 
30200             }, this);
30201         }
30202         
30203         if(this.loadMask){
30204             this.maskEl = this.el;
30205         }
30206     },
30207     
30208     initEvents : function()
30209     {
30210         this.urlAPI = (window.createObjectURL && window) || 
30211                                 (window.URL && URL.revokeObjectURL && URL) || 
30212                                 (window.webkitURL && webkitURL);
30213                         
30214         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30215         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30216         
30217         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30218         this.selectorEl.hide();
30219         
30220         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30221         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30222         
30223         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30224         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30225         this.thumbEl.hide();
30226         
30227         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30228         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30229         
30230         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30231         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30232         this.errorEl.hide();
30233         
30234         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30235         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30236         this.footerEl.hide();
30237         
30238         this.setThumbBoxSize();
30239         
30240         this.bind();
30241         
30242         this.resize();
30243         
30244         this.fireEvent('initial', this);
30245     },
30246
30247     bind : function()
30248     {
30249         var _this = this;
30250         
30251         window.addEventListener("resize", function() { _this.resize(); } );
30252         
30253         this.bodyEl.on('click', this.beforeSelectFile, this);
30254         
30255         if(Roo.isTouch){
30256             this.bodyEl.on('touchstart', this.onTouchStart, this);
30257             this.bodyEl.on('touchmove', this.onTouchMove, this);
30258             this.bodyEl.on('touchend', this.onTouchEnd, this);
30259         }
30260         
30261         if(!Roo.isTouch){
30262             this.bodyEl.on('mousedown', this.onMouseDown, this);
30263             this.bodyEl.on('mousemove', this.onMouseMove, this);
30264             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30265             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30266             Roo.get(document).on('mouseup', this.onMouseUp, this);
30267         }
30268         
30269         this.selectorEl.on('change', this.onFileSelected, this);
30270     },
30271     
30272     reset : function()
30273     {    
30274         this.scale = 0;
30275         this.baseScale = 1;
30276         this.rotate = 0;
30277         this.baseRotate = 1;
30278         this.dragable = false;
30279         this.pinching = false;
30280         this.mouseX = 0;
30281         this.mouseY = 0;
30282         this.cropData = false;
30283         this.notifyEl.dom.innerHTML = this.emptyText;
30284         
30285         this.selectorEl.dom.value = '';
30286         
30287     },
30288     
30289     resize : function()
30290     {
30291         if(this.fireEvent('resize', this) != false){
30292             this.setThumbBoxPosition();
30293             this.setCanvasPosition();
30294         }
30295     },
30296     
30297     onFooterButtonClick : function(e, el, o, type)
30298     {
30299         switch (type) {
30300             case 'rotate-left' :
30301                 this.onRotateLeft(e);
30302                 break;
30303             case 'rotate-right' :
30304                 this.onRotateRight(e);
30305                 break;
30306             case 'picture' :
30307                 this.beforeSelectFile(e);
30308                 break;
30309             case 'trash' :
30310                 this.trash(e);
30311                 break;
30312             case 'crop' :
30313                 this.crop(e);
30314                 break;
30315             case 'download' :
30316                 this.download(e);
30317                 break;
30318             default :
30319                 break;
30320         }
30321         
30322         this.fireEvent('footerbuttonclick', this, type);
30323     },
30324     
30325     beforeSelectFile : function(e)
30326     {
30327         e.preventDefault();
30328         
30329         if(this.fireEvent('beforeselectfile', this) != false){
30330             this.selectorEl.dom.click();
30331         }
30332     },
30333     
30334     onFileSelected : function(e)
30335     {
30336         e.preventDefault();
30337         
30338         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30339             return;
30340         }
30341         
30342         var file = this.selectorEl.dom.files[0];
30343         
30344         if(this.fireEvent('inspect', this, file) != false){
30345             this.prepare(file);
30346         }
30347         
30348     },
30349     
30350     trash : function(e)
30351     {
30352         this.fireEvent('trash', this);
30353     },
30354     
30355     download : function(e)
30356     {
30357         this.fireEvent('download', this);
30358     },
30359     
30360     loadCanvas : function(src)
30361     {   
30362         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30363             
30364             this.reset();
30365             
30366             this.imageEl = document.createElement('img');
30367             
30368             var _this = this;
30369             
30370             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30371             
30372             this.imageEl.src = src;
30373         }
30374     },
30375     
30376     onLoadCanvas : function()
30377     {   
30378         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30379         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30380         
30381         this.bodyEl.un('click', this.beforeSelectFile, this);
30382         
30383         this.notifyEl.hide();
30384         this.thumbEl.show();
30385         this.footerEl.show();
30386         
30387         this.baseRotateLevel();
30388         
30389         if(this.isDocument){
30390             this.setThumbBoxSize();
30391         }
30392         
30393         this.setThumbBoxPosition();
30394         
30395         this.baseScaleLevel();
30396         
30397         this.draw();
30398         
30399         this.resize();
30400         
30401         this.canvasLoaded = true;
30402         
30403         if(this.loadMask){
30404             this.maskEl.unmask();
30405         }
30406         
30407     },
30408     
30409     setCanvasPosition : function()
30410     {   
30411         if(!this.canvasEl){
30412             return;
30413         }
30414         
30415         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30416         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30417         
30418         this.previewEl.setLeft(pw);
30419         this.previewEl.setTop(ph);
30420         
30421     },
30422     
30423     onMouseDown : function(e)
30424     {   
30425         e.stopEvent();
30426         
30427         this.dragable = true;
30428         this.pinching = false;
30429         
30430         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30431             this.dragable = false;
30432             return;
30433         }
30434         
30435         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30436         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30437         
30438     },
30439     
30440     onMouseMove : function(e)
30441     {   
30442         e.stopEvent();
30443         
30444         if(!this.canvasLoaded){
30445             return;
30446         }
30447         
30448         if (!this.dragable){
30449             return;
30450         }
30451         
30452         var minX = Math.ceil(this.thumbEl.getLeft(true));
30453         var minY = Math.ceil(this.thumbEl.getTop(true));
30454         
30455         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30456         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30457         
30458         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30459         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30460         
30461         x = x - this.mouseX;
30462         y = y - this.mouseY;
30463         
30464         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30465         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30466         
30467         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30468         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30469         
30470         this.previewEl.setLeft(bgX);
30471         this.previewEl.setTop(bgY);
30472         
30473         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30474         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30475     },
30476     
30477     onMouseUp : function(e)
30478     {   
30479         e.stopEvent();
30480         
30481         this.dragable = false;
30482     },
30483     
30484     onMouseWheel : function(e)
30485     {   
30486         e.stopEvent();
30487         
30488         this.startScale = this.scale;
30489         
30490         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30491         
30492         if(!this.zoomable()){
30493             this.scale = this.startScale;
30494             return;
30495         }
30496         
30497         this.draw();
30498         
30499         return;
30500     },
30501     
30502     zoomable : function()
30503     {
30504         var minScale = this.thumbEl.getWidth() / this.minWidth;
30505         
30506         if(this.minWidth < this.minHeight){
30507             minScale = this.thumbEl.getHeight() / this.minHeight;
30508         }
30509         
30510         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30511         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30512         
30513         if(
30514                 this.isDocument &&
30515                 (this.rotate == 0 || this.rotate == 180) && 
30516                 (
30517                     width > this.imageEl.OriginWidth || 
30518                     height > this.imageEl.OriginHeight ||
30519                     (width < this.minWidth && height < this.minHeight)
30520                 )
30521         ){
30522             return false;
30523         }
30524         
30525         if(
30526                 this.isDocument &&
30527                 (this.rotate == 90 || this.rotate == 270) && 
30528                 (
30529                     width > this.imageEl.OriginWidth || 
30530                     height > this.imageEl.OriginHeight ||
30531                     (width < this.minHeight && height < this.minWidth)
30532                 )
30533         ){
30534             return false;
30535         }
30536         
30537         if(
30538                 !this.isDocument &&
30539                 (this.rotate == 0 || this.rotate == 180) && 
30540                 (
30541                     width < this.minWidth || 
30542                     width > this.imageEl.OriginWidth || 
30543                     height < this.minHeight || 
30544                     height > this.imageEl.OriginHeight
30545                 )
30546         ){
30547             return false;
30548         }
30549         
30550         if(
30551                 !this.isDocument &&
30552                 (this.rotate == 90 || this.rotate == 270) && 
30553                 (
30554                     width < this.minHeight || 
30555                     width > this.imageEl.OriginWidth || 
30556                     height < this.minWidth || 
30557                     height > this.imageEl.OriginHeight
30558                 )
30559         ){
30560             return false;
30561         }
30562         
30563         return true;
30564         
30565     },
30566     
30567     onRotateLeft : function(e)
30568     {   
30569         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30570             
30571             var minScale = this.thumbEl.getWidth() / this.minWidth;
30572             
30573             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30574             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30575             
30576             this.startScale = this.scale;
30577             
30578             while (this.getScaleLevel() < minScale){
30579             
30580                 this.scale = this.scale + 1;
30581                 
30582                 if(!this.zoomable()){
30583                     break;
30584                 }
30585                 
30586                 if(
30587                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30588                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30589                 ){
30590                     continue;
30591                 }
30592                 
30593                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30594
30595                 this.draw();
30596                 
30597                 return;
30598             }
30599             
30600             this.scale = this.startScale;
30601             
30602             this.onRotateFail();
30603             
30604             return false;
30605         }
30606         
30607         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30608
30609         if(this.isDocument){
30610             this.setThumbBoxSize();
30611             this.setThumbBoxPosition();
30612             this.setCanvasPosition();
30613         }
30614         
30615         this.draw();
30616         
30617         this.fireEvent('rotate', this, 'left');
30618         
30619     },
30620     
30621     onRotateRight : function(e)
30622     {
30623         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30624             
30625             var minScale = this.thumbEl.getWidth() / this.minWidth;
30626         
30627             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30628             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30629             
30630             this.startScale = this.scale;
30631             
30632             while (this.getScaleLevel() < minScale){
30633             
30634                 this.scale = this.scale + 1;
30635                 
30636                 if(!this.zoomable()){
30637                     break;
30638                 }
30639                 
30640                 if(
30641                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30642                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30643                 ){
30644                     continue;
30645                 }
30646                 
30647                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30648
30649                 this.draw();
30650                 
30651                 return;
30652             }
30653             
30654             this.scale = this.startScale;
30655             
30656             this.onRotateFail();
30657             
30658             return false;
30659         }
30660         
30661         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30662
30663         if(this.isDocument){
30664             this.setThumbBoxSize();
30665             this.setThumbBoxPosition();
30666             this.setCanvasPosition();
30667         }
30668         
30669         this.draw();
30670         
30671         this.fireEvent('rotate', this, 'right');
30672     },
30673     
30674     onRotateFail : function()
30675     {
30676         this.errorEl.show(true);
30677         
30678         var _this = this;
30679         
30680         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30681     },
30682     
30683     draw : function()
30684     {
30685         this.previewEl.dom.innerHTML = '';
30686         
30687         var canvasEl = document.createElement("canvas");
30688         
30689         var contextEl = canvasEl.getContext("2d");
30690         
30691         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30692         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30693         var center = this.imageEl.OriginWidth / 2;
30694         
30695         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30696             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30697             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30698             center = this.imageEl.OriginHeight / 2;
30699         }
30700         
30701         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30702         
30703         contextEl.translate(center, center);
30704         contextEl.rotate(this.rotate * Math.PI / 180);
30705
30706         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30707         
30708         this.canvasEl = document.createElement("canvas");
30709         
30710         this.contextEl = this.canvasEl.getContext("2d");
30711         
30712         switch (this.rotate) {
30713             case 0 :
30714                 
30715                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30716                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30717                 
30718                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30719                 
30720                 break;
30721             case 90 : 
30722                 
30723                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30724                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30725                 
30726                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30727                     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);
30728                     break;
30729                 }
30730                 
30731                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30732                 
30733                 break;
30734             case 180 :
30735                 
30736                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30737                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30738                 
30739                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30740                     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);
30741                     break;
30742                 }
30743                 
30744                 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);
30745                 
30746                 break;
30747             case 270 :
30748                 
30749                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30750                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30751         
30752                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30753                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30754                     break;
30755                 }
30756                 
30757                 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);
30758                 
30759                 break;
30760             default : 
30761                 break;
30762         }
30763         
30764         this.previewEl.appendChild(this.canvasEl);
30765         
30766         this.setCanvasPosition();
30767     },
30768     
30769     crop : function()
30770     {
30771         if(!this.canvasLoaded){
30772             return;
30773         }
30774         
30775         var imageCanvas = document.createElement("canvas");
30776         
30777         var imageContext = imageCanvas.getContext("2d");
30778         
30779         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30780         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30781         
30782         var center = imageCanvas.width / 2;
30783         
30784         imageContext.translate(center, center);
30785         
30786         imageContext.rotate(this.rotate * Math.PI / 180);
30787         
30788         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30789         
30790         var canvas = document.createElement("canvas");
30791         
30792         var context = canvas.getContext("2d");
30793                 
30794         canvas.width = this.minWidth;
30795         canvas.height = this.minHeight;
30796
30797         switch (this.rotate) {
30798             case 0 :
30799                 
30800                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30801                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30802                 
30803                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30804                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30805                 
30806                 var targetWidth = this.minWidth - 2 * x;
30807                 var targetHeight = this.minHeight - 2 * y;
30808                 
30809                 var scale = 1;
30810                 
30811                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30812                     scale = targetWidth / width;
30813                 }
30814                 
30815                 if(x > 0 && y == 0){
30816                     scale = targetHeight / height;
30817                 }
30818                 
30819                 if(x > 0 && y > 0){
30820                     scale = targetWidth / width;
30821                     
30822                     if(width < height){
30823                         scale = targetHeight / height;
30824                     }
30825                 }
30826                 
30827                 context.scale(scale, scale);
30828                 
30829                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30830                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30831
30832                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30833                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30834
30835                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30836                 
30837                 break;
30838             case 90 : 
30839                 
30840                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30841                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30842                 
30843                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30844                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30845                 
30846                 var targetWidth = this.minWidth - 2 * x;
30847                 var targetHeight = this.minHeight - 2 * y;
30848                 
30849                 var scale = 1;
30850                 
30851                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30852                     scale = targetWidth / width;
30853                 }
30854                 
30855                 if(x > 0 && y == 0){
30856                     scale = targetHeight / height;
30857                 }
30858                 
30859                 if(x > 0 && y > 0){
30860                     scale = targetWidth / width;
30861                     
30862                     if(width < height){
30863                         scale = targetHeight / height;
30864                     }
30865                 }
30866                 
30867                 context.scale(scale, scale);
30868                 
30869                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30870                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30871
30872                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30873                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30874                 
30875                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30876                 
30877                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30878                 
30879                 break;
30880             case 180 :
30881                 
30882                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30883                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30884                 
30885                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30886                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30887                 
30888                 var targetWidth = this.minWidth - 2 * x;
30889                 var targetHeight = this.minHeight - 2 * y;
30890                 
30891                 var scale = 1;
30892                 
30893                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30894                     scale = targetWidth / width;
30895                 }
30896                 
30897                 if(x > 0 && y == 0){
30898                     scale = targetHeight / height;
30899                 }
30900                 
30901                 if(x > 0 && y > 0){
30902                     scale = targetWidth / width;
30903                     
30904                     if(width < height){
30905                         scale = targetHeight / height;
30906                     }
30907                 }
30908                 
30909                 context.scale(scale, scale);
30910                 
30911                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30912                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30913
30914                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30915                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30916
30917                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30918                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30919                 
30920                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30921                 
30922                 break;
30923             case 270 :
30924                 
30925                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30926                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30927                 
30928                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30929                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30930                 
30931                 var targetWidth = this.minWidth - 2 * x;
30932                 var targetHeight = this.minHeight - 2 * y;
30933                 
30934                 var scale = 1;
30935                 
30936                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30937                     scale = targetWidth / width;
30938                 }
30939                 
30940                 if(x > 0 && y == 0){
30941                     scale = targetHeight / height;
30942                 }
30943                 
30944                 if(x > 0 && y > 0){
30945                     scale = targetWidth / width;
30946                     
30947                     if(width < height){
30948                         scale = targetHeight / height;
30949                     }
30950                 }
30951                 
30952                 context.scale(scale, scale);
30953                 
30954                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30955                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30956
30957                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30958                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30959                 
30960                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30961                 
30962                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30963                 
30964                 break;
30965             default : 
30966                 break;
30967         }
30968         
30969         this.cropData = canvas.toDataURL(this.cropType);
30970         
30971         if(this.fireEvent('crop', this, this.cropData) !== false){
30972             this.process(this.file, this.cropData);
30973         }
30974         
30975         return;
30976         
30977     },
30978     
30979     setThumbBoxSize : function()
30980     {
30981         var width, height;
30982         
30983         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30984             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30985             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30986             
30987             this.minWidth = width;
30988             this.minHeight = height;
30989             
30990             if(this.rotate == 90 || this.rotate == 270){
30991                 this.minWidth = height;
30992                 this.minHeight = width;
30993             }
30994         }
30995         
30996         height = 300;
30997         width = Math.ceil(this.minWidth * height / this.minHeight);
30998         
30999         if(this.minWidth > this.minHeight){
31000             width = 300;
31001             height = Math.ceil(this.minHeight * width / this.minWidth);
31002         }
31003         
31004         this.thumbEl.setStyle({
31005             width : width + 'px',
31006             height : height + 'px'
31007         });
31008
31009         return;
31010             
31011     },
31012     
31013     setThumbBoxPosition : function()
31014     {
31015         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31016         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31017         
31018         this.thumbEl.setLeft(x);
31019         this.thumbEl.setTop(y);
31020         
31021     },
31022     
31023     baseRotateLevel : function()
31024     {
31025         this.baseRotate = 1;
31026         
31027         if(
31028                 typeof(this.exif) != 'undefined' &&
31029                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31030                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31031         ){
31032             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31033         }
31034         
31035         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31036         
31037     },
31038     
31039     baseScaleLevel : function()
31040     {
31041         var width, height;
31042         
31043         if(this.isDocument){
31044             
31045             if(this.baseRotate == 6 || this.baseRotate == 8){
31046             
31047                 height = this.thumbEl.getHeight();
31048                 this.baseScale = height / this.imageEl.OriginWidth;
31049
31050                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31051                     width = this.thumbEl.getWidth();
31052                     this.baseScale = width / this.imageEl.OriginHeight;
31053                 }
31054
31055                 return;
31056             }
31057
31058             height = this.thumbEl.getHeight();
31059             this.baseScale = height / this.imageEl.OriginHeight;
31060
31061             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31062                 width = this.thumbEl.getWidth();
31063                 this.baseScale = width / this.imageEl.OriginWidth;
31064             }
31065
31066             return;
31067         }
31068         
31069         if(this.baseRotate == 6 || this.baseRotate == 8){
31070             
31071             width = this.thumbEl.getHeight();
31072             this.baseScale = width / this.imageEl.OriginHeight;
31073             
31074             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31075                 height = this.thumbEl.getWidth();
31076                 this.baseScale = height / this.imageEl.OriginHeight;
31077             }
31078             
31079             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31080                 height = this.thumbEl.getWidth();
31081                 this.baseScale = height / this.imageEl.OriginHeight;
31082                 
31083                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31084                     width = this.thumbEl.getHeight();
31085                     this.baseScale = width / this.imageEl.OriginWidth;
31086                 }
31087             }
31088             
31089             return;
31090         }
31091         
31092         width = this.thumbEl.getWidth();
31093         this.baseScale = width / this.imageEl.OriginWidth;
31094         
31095         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31096             height = this.thumbEl.getHeight();
31097             this.baseScale = height / this.imageEl.OriginHeight;
31098         }
31099         
31100         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31101             
31102             height = this.thumbEl.getHeight();
31103             this.baseScale = height / this.imageEl.OriginHeight;
31104             
31105             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31106                 width = this.thumbEl.getWidth();
31107                 this.baseScale = width / this.imageEl.OriginWidth;
31108             }
31109             
31110         }
31111         
31112         return;
31113     },
31114     
31115     getScaleLevel : function()
31116     {
31117         return this.baseScale * Math.pow(1.1, this.scale);
31118     },
31119     
31120     onTouchStart : function(e)
31121     {
31122         if(!this.canvasLoaded){
31123             this.beforeSelectFile(e);
31124             return;
31125         }
31126         
31127         var touches = e.browserEvent.touches;
31128         
31129         if(!touches){
31130             return;
31131         }
31132         
31133         if(touches.length == 1){
31134             this.onMouseDown(e);
31135             return;
31136         }
31137         
31138         if(touches.length != 2){
31139             return;
31140         }
31141         
31142         var coords = [];
31143         
31144         for(var i = 0, finger; finger = touches[i]; i++){
31145             coords.push(finger.pageX, finger.pageY);
31146         }
31147         
31148         var x = Math.pow(coords[0] - coords[2], 2);
31149         var y = Math.pow(coords[1] - coords[3], 2);
31150         
31151         this.startDistance = Math.sqrt(x + y);
31152         
31153         this.startScale = this.scale;
31154         
31155         this.pinching = true;
31156         this.dragable = false;
31157         
31158     },
31159     
31160     onTouchMove : function(e)
31161     {
31162         if(!this.pinching && !this.dragable){
31163             return;
31164         }
31165         
31166         var touches = e.browserEvent.touches;
31167         
31168         if(!touches){
31169             return;
31170         }
31171         
31172         if(this.dragable){
31173             this.onMouseMove(e);
31174             return;
31175         }
31176         
31177         var coords = [];
31178         
31179         for(var i = 0, finger; finger = touches[i]; i++){
31180             coords.push(finger.pageX, finger.pageY);
31181         }
31182         
31183         var x = Math.pow(coords[0] - coords[2], 2);
31184         var y = Math.pow(coords[1] - coords[3], 2);
31185         
31186         this.endDistance = Math.sqrt(x + y);
31187         
31188         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31189         
31190         if(!this.zoomable()){
31191             this.scale = this.startScale;
31192             return;
31193         }
31194         
31195         this.draw();
31196         
31197     },
31198     
31199     onTouchEnd : function(e)
31200     {
31201         this.pinching = false;
31202         this.dragable = false;
31203         
31204     },
31205     
31206     process : function(file, crop)
31207     {
31208         if(this.loadMask){
31209             this.maskEl.mask(this.loadingText);
31210         }
31211         
31212         this.xhr = new XMLHttpRequest();
31213         
31214         file.xhr = this.xhr;
31215
31216         this.xhr.open(this.method, this.url, true);
31217         
31218         var headers = {
31219             "Accept": "application/json",
31220             "Cache-Control": "no-cache",
31221             "X-Requested-With": "XMLHttpRequest"
31222         };
31223         
31224         for (var headerName in headers) {
31225             var headerValue = headers[headerName];
31226             if (headerValue) {
31227                 this.xhr.setRequestHeader(headerName, headerValue);
31228             }
31229         }
31230         
31231         var _this = this;
31232         
31233         this.xhr.onload = function()
31234         {
31235             _this.xhrOnLoad(_this.xhr);
31236         }
31237         
31238         this.xhr.onerror = function()
31239         {
31240             _this.xhrOnError(_this.xhr);
31241         }
31242         
31243         var formData = new FormData();
31244
31245         formData.append('returnHTML', 'NO');
31246         
31247         if(crop){
31248             formData.append('crop', crop);
31249         }
31250         
31251         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31252             formData.append(this.paramName, file, file.name);
31253         }
31254         
31255         if(typeof(file.filename) != 'undefined'){
31256             formData.append('filename', file.filename);
31257         }
31258         
31259         if(typeof(file.mimetype) != 'undefined'){
31260             formData.append('mimetype', file.mimetype);
31261         }
31262         
31263         if(this.fireEvent('arrange', this, formData) != false){
31264             this.xhr.send(formData);
31265         };
31266     },
31267     
31268     xhrOnLoad : function(xhr)
31269     {
31270         if(this.loadMask){
31271             this.maskEl.unmask();
31272         }
31273         
31274         if (xhr.readyState !== 4) {
31275             this.fireEvent('exception', this, xhr);
31276             return;
31277         }
31278
31279         var response = Roo.decode(xhr.responseText);
31280         
31281         if(!response.success){
31282             this.fireEvent('exception', this, xhr);
31283             return;
31284         }
31285         
31286         var response = Roo.decode(xhr.responseText);
31287         
31288         this.fireEvent('upload', this, response);
31289         
31290     },
31291     
31292     xhrOnError : function()
31293     {
31294         if(this.loadMask){
31295             this.maskEl.unmask();
31296         }
31297         
31298         Roo.log('xhr on error');
31299         
31300         var response = Roo.decode(xhr.responseText);
31301           
31302         Roo.log(response);
31303         
31304     },
31305     
31306     prepare : function(file)
31307     {   
31308         if(this.loadMask){
31309             this.maskEl.mask(this.loadingText);
31310         }
31311         
31312         this.file = false;
31313         this.exif = {};
31314         
31315         if(typeof(file) === 'string'){
31316             this.loadCanvas(file);
31317             return;
31318         }
31319         
31320         if(!file || !this.urlAPI){
31321             return;
31322         }
31323         
31324         this.file = file;
31325         this.cropType = file.type;
31326         
31327         var _this = this;
31328         
31329         if(this.fireEvent('prepare', this, this.file) != false){
31330             
31331             var reader = new FileReader();
31332             
31333             reader.onload = function (e) {
31334                 if (e.target.error) {
31335                     Roo.log(e.target.error);
31336                     return;
31337                 }
31338                 
31339                 var buffer = e.target.result,
31340                     dataView = new DataView(buffer),
31341                     offset = 2,
31342                     maxOffset = dataView.byteLength - 4,
31343                     markerBytes,
31344                     markerLength;
31345                 
31346                 if (dataView.getUint16(0) === 0xffd8) {
31347                     while (offset < maxOffset) {
31348                         markerBytes = dataView.getUint16(offset);
31349                         
31350                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31351                             markerLength = dataView.getUint16(offset + 2) + 2;
31352                             if (offset + markerLength > dataView.byteLength) {
31353                                 Roo.log('Invalid meta data: Invalid segment size.');
31354                                 break;
31355                             }
31356                             
31357                             if(markerBytes == 0xffe1){
31358                                 _this.parseExifData(
31359                                     dataView,
31360                                     offset,
31361                                     markerLength
31362                                 );
31363                             }
31364                             
31365                             offset += markerLength;
31366                             
31367                             continue;
31368                         }
31369                         
31370                         break;
31371                     }
31372                     
31373                 }
31374                 
31375                 var url = _this.urlAPI.createObjectURL(_this.file);
31376                 
31377                 _this.loadCanvas(url);
31378                 
31379                 return;
31380             }
31381             
31382             reader.readAsArrayBuffer(this.file);
31383             
31384         }
31385         
31386     },
31387     
31388     parseExifData : function(dataView, offset, length)
31389     {
31390         var tiffOffset = offset + 10,
31391             littleEndian,
31392             dirOffset;
31393     
31394         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31395             // No Exif data, might be XMP data instead
31396             return;
31397         }
31398         
31399         // Check for the ASCII code for "Exif" (0x45786966):
31400         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31401             // No Exif data, might be XMP data instead
31402             return;
31403         }
31404         if (tiffOffset + 8 > dataView.byteLength) {
31405             Roo.log('Invalid Exif data: Invalid segment size.');
31406             return;
31407         }
31408         // Check for the two null bytes:
31409         if (dataView.getUint16(offset + 8) !== 0x0000) {
31410             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31411             return;
31412         }
31413         // Check the byte alignment:
31414         switch (dataView.getUint16(tiffOffset)) {
31415         case 0x4949:
31416             littleEndian = true;
31417             break;
31418         case 0x4D4D:
31419             littleEndian = false;
31420             break;
31421         default:
31422             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31423             return;
31424         }
31425         // Check for the TIFF tag marker (0x002A):
31426         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31427             Roo.log('Invalid Exif data: Missing TIFF marker.');
31428             return;
31429         }
31430         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31431         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31432         
31433         this.parseExifTags(
31434             dataView,
31435             tiffOffset,
31436             tiffOffset + dirOffset,
31437             littleEndian
31438         );
31439     },
31440     
31441     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31442     {
31443         var tagsNumber,
31444             dirEndOffset,
31445             i;
31446         if (dirOffset + 6 > dataView.byteLength) {
31447             Roo.log('Invalid Exif data: Invalid directory offset.');
31448             return;
31449         }
31450         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31451         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31452         if (dirEndOffset + 4 > dataView.byteLength) {
31453             Roo.log('Invalid Exif data: Invalid directory size.');
31454             return;
31455         }
31456         for (i = 0; i < tagsNumber; i += 1) {
31457             this.parseExifTag(
31458                 dataView,
31459                 tiffOffset,
31460                 dirOffset + 2 + 12 * i, // tag offset
31461                 littleEndian
31462             );
31463         }
31464         // Return the offset to the next directory:
31465         return dataView.getUint32(dirEndOffset, littleEndian);
31466     },
31467     
31468     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31469     {
31470         var tag = dataView.getUint16(offset, littleEndian);
31471         
31472         this.exif[tag] = this.getExifValue(
31473             dataView,
31474             tiffOffset,
31475             offset,
31476             dataView.getUint16(offset + 2, littleEndian), // tag type
31477             dataView.getUint32(offset + 4, littleEndian), // tag length
31478             littleEndian
31479         );
31480     },
31481     
31482     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31483     {
31484         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31485             tagSize,
31486             dataOffset,
31487             values,
31488             i,
31489             str,
31490             c;
31491     
31492         if (!tagType) {
31493             Roo.log('Invalid Exif data: Invalid tag type.');
31494             return;
31495         }
31496         
31497         tagSize = tagType.size * length;
31498         // Determine if the value is contained in the dataOffset bytes,
31499         // or if the value at the dataOffset is a pointer to the actual data:
31500         dataOffset = tagSize > 4 ?
31501                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31502         if (dataOffset + tagSize > dataView.byteLength) {
31503             Roo.log('Invalid Exif data: Invalid data offset.');
31504             return;
31505         }
31506         if (length === 1) {
31507             return tagType.getValue(dataView, dataOffset, littleEndian);
31508         }
31509         values = [];
31510         for (i = 0; i < length; i += 1) {
31511             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31512         }
31513         
31514         if (tagType.ascii) {
31515             str = '';
31516             // Concatenate the chars:
31517             for (i = 0; i < values.length; i += 1) {
31518                 c = values[i];
31519                 // Ignore the terminating NULL byte(s):
31520                 if (c === '\u0000') {
31521                     break;
31522                 }
31523                 str += c;
31524             }
31525             return str;
31526         }
31527         return values;
31528     }
31529     
31530 });
31531
31532 Roo.apply(Roo.bootstrap.UploadCropbox, {
31533     tags : {
31534         'Orientation': 0x0112
31535     },
31536     
31537     Orientation: {
31538             1: 0, //'top-left',
31539 //            2: 'top-right',
31540             3: 180, //'bottom-right',
31541 //            4: 'bottom-left',
31542 //            5: 'left-top',
31543             6: 90, //'right-top',
31544 //            7: 'right-bottom',
31545             8: 270 //'left-bottom'
31546     },
31547     
31548     exifTagTypes : {
31549         // byte, 8-bit unsigned int:
31550         1: {
31551             getValue: function (dataView, dataOffset) {
31552                 return dataView.getUint8(dataOffset);
31553             },
31554             size: 1
31555         },
31556         // ascii, 8-bit byte:
31557         2: {
31558             getValue: function (dataView, dataOffset) {
31559                 return String.fromCharCode(dataView.getUint8(dataOffset));
31560             },
31561             size: 1,
31562             ascii: true
31563         },
31564         // short, 16 bit int:
31565         3: {
31566             getValue: function (dataView, dataOffset, littleEndian) {
31567                 return dataView.getUint16(dataOffset, littleEndian);
31568             },
31569             size: 2
31570         },
31571         // long, 32 bit int:
31572         4: {
31573             getValue: function (dataView, dataOffset, littleEndian) {
31574                 return dataView.getUint32(dataOffset, littleEndian);
31575             },
31576             size: 4
31577         },
31578         // rational = two long values, first is numerator, second is denominator:
31579         5: {
31580             getValue: function (dataView, dataOffset, littleEndian) {
31581                 return dataView.getUint32(dataOffset, littleEndian) /
31582                     dataView.getUint32(dataOffset + 4, littleEndian);
31583             },
31584             size: 8
31585         },
31586         // slong, 32 bit signed int:
31587         9: {
31588             getValue: function (dataView, dataOffset, littleEndian) {
31589                 return dataView.getInt32(dataOffset, littleEndian);
31590             },
31591             size: 4
31592         },
31593         // srational, two slongs, first is numerator, second is denominator:
31594         10: {
31595             getValue: function (dataView, dataOffset, littleEndian) {
31596                 return dataView.getInt32(dataOffset, littleEndian) /
31597                     dataView.getInt32(dataOffset + 4, littleEndian);
31598             },
31599             size: 8
31600         }
31601     },
31602     
31603     footer : {
31604         STANDARD : [
31605             {
31606                 tag : 'div',
31607                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31608                 action : 'rotate-left',
31609                 cn : [
31610                     {
31611                         tag : 'button',
31612                         cls : 'btn btn-default',
31613                         html : '<i class="fa fa-undo"></i>'
31614                     }
31615                 ]
31616             },
31617             {
31618                 tag : 'div',
31619                 cls : 'btn-group roo-upload-cropbox-picture',
31620                 action : 'picture',
31621                 cn : [
31622                     {
31623                         tag : 'button',
31624                         cls : 'btn btn-default',
31625                         html : '<i class="fa fa-picture-o"></i>'
31626                     }
31627                 ]
31628             },
31629             {
31630                 tag : 'div',
31631                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31632                 action : 'rotate-right',
31633                 cn : [
31634                     {
31635                         tag : 'button',
31636                         cls : 'btn btn-default',
31637                         html : '<i class="fa fa-repeat"></i>'
31638                     }
31639                 ]
31640             }
31641         ],
31642         DOCUMENT : [
31643             {
31644                 tag : 'div',
31645                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31646                 action : 'rotate-left',
31647                 cn : [
31648                     {
31649                         tag : 'button',
31650                         cls : 'btn btn-default',
31651                         html : '<i class="fa fa-undo"></i>'
31652                     }
31653                 ]
31654             },
31655             {
31656                 tag : 'div',
31657                 cls : 'btn-group roo-upload-cropbox-download',
31658                 action : 'download',
31659                 cn : [
31660                     {
31661                         tag : 'button',
31662                         cls : 'btn btn-default',
31663                         html : '<i class="fa fa-download"></i>'
31664                     }
31665                 ]
31666             },
31667             {
31668                 tag : 'div',
31669                 cls : 'btn-group roo-upload-cropbox-crop',
31670                 action : 'crop',
31671                 cn : [
31672                     {
31673                         tag : 'button',
31674                         cls : 'btn btn-default',
31675                         html : '<i class="fa fa-crop"></i>'
31676                     }
31677                 ]
31678             },
31679             {
31680                 tag : 'div',
31681                 cls : 'btn-group roo-upload-cropbox-trash',
31682                 action : 'trash',
31683                 cn : [
31684                     {
31685                         tag : 'button',
31686                         cls : 'btn btn-default',
31687                         html : '<i class="fa fa-trash"></i>'
31688                     }
31689                 ]
31690             },
31691             {
31692                 tag : 'div',
31693                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31694                 action : 'rotate-right',
31695                 cn : [
31696                     {
31697                         tag : 'button',
31698                         cls : 'btn btn-default',
31699                         html : '<i class="fa fa-repeat"></i>'
31700                     }
31701                 ]
31702             }
31703         ],
31704         ROTATOR : [
31705             {
31706                 tag : 'div',
31707                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31708                 action : 'rotate-left',
31709                 cn : [
31710                     {
31711                         tag : 'button',
31712                         cls : 'btn btn-default',
31713                         html : '<i class="fa fa-undo"></i>'
31714                     }
31715                 ]
31716             },
31717             {
31718                 tag : 'div',
31719                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31720                 action : 'rotate-right',
31721                 cn : [
31722                     {
31723                         tag : 'button',
31724                         cls : 'btn btn-default',
31725                         html : '<i class="fa fa-repeat"></i>'
31726                     }
31727                 ]
31728             }
31729         ]
31730     }
31731 });
31732
31733 /*
31734 * Licence: LGPL
31735 */
31736
31737 /**
31738  * @class Roo.bootstrap.DocumentManager
31739  * @extends Roo.bootstrap.Component
31740  * Bootstrap DocumentManager class
31741  * @cfg {String} paramName default 'imageUpload'
31742  * @cfg {String} toolTipName default 'filename'
31743  * @cfg {String} method default POST
31744  * @cfg {String} url action url
31745  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31746  * @cfg {Boolean} multiple multiple upload default true
31747  * @cfg {Number} thumbSize default 300
31748  * @cfg {String} fieldLabel
31749  * @cfg {Number} labelWidth default 4
31750  * @cfg {String} labelAlign (left|top) default left
31751  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31752 * @cfg {Number} labellg set the width of label (1-12)
31753  * @cfg {Number} labelmd set the width of label (1-12)
31754  * @cfg {Number} labelsm set the width of label (1-12)
31755  * @cfg {Number} labelxs set the width of label (1-12)
31756  * 
31757  * @constructor
31758  * Create a new DocumentManager
31759  * @param {Object} config The config object
31760  */
31761
31762 Roo.bootstrap.DocumentManager = function(config){
31763     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31764     
31765     this.files = [];
31766     this.delegates = [];
31767     
31768     this.addEvents({
31769         /**
31770          * @event initial
31771          * Fire when initial the DocumentManager
31772          * @param {Roo.bootstrap.DocumentManager} this
31773          */
31774         "initial" : true,
31775         /**
31776          * @event inspect
31777          * inspect selected file
31778          * @param {Roo.bootstrap.DocumentManager} this
31779          * @param {File} file
31780          */
31781         "inspect" : true,
31782         /**
31783          * @event exception
31784          * Fire when xhr load exception
31785          * @param {Roo.bootstrap.DocumentManager} this
31786          * @param {XMLHttpRequest} xhr
31787          */
31788         "exception" : true,
31789         /**
31790          * @event afterupload
31791          * Fire when xhr load exception
31792          * @param {Roo.bootstrap.DocumentManager} this
31793          * @param {XMLHttpRequest} xhr
31794          */
31795         "afterupload" : true,
31796         /**
31797          * @event prepare
31798          * prepare the form data
31799          * @param {Roo.bootstrap.DocumentManager} this
31800          * @param {Object} formData
31801          */
31802         "prepare" : true,
31803         /**
31804          * @event remove
31805          * Fire when remove the file
31806          * @param {Roo.bootstrap.DocumentManager} this
31807          * @param {Object} file
31808          */
31809         "remove" : true,
31810         /**
31811          * @event refresh
31812          * Fire after refresh the file
31813          * @param {Roo.bootstrap.DocumentManager} this
31814          */
31815         "refresh" : true,
31816         /**
31817          * @event click
31818          * Fire after click the image
31819          * @param {Roo.bootstrap.DocumentManager} this
31820          * @param {Object} file
31821          */
31822         "click" : true,
31823         /**
31824          * @event edit
31825          * Fire when upload a image and editable set to true
31826          * @param {Roo.bootstrap.DocumentManager} this
31827          * @param {Object} file
31828          */
31829         "edit" : true,
31830         /**
31831          * @event beforeselectfile
31832          * Fire before select file
31833          * @param {Roo.bootstrap.DocumentManager} this
31834          */
31835         "beforeselectfile" : true,
31836         /**
31837          * @event process
31838          * Fire before process file
31839          * @param {Roo.bootstrap.DocumentManager} this
31840          * @param {Object} file
31841          */
31842         "process" : true,
31843         /**
31844          * @event previewrendered
31845          * Fire when preview rendered
31846          * @param {Roo.bootstrap.DocumentManager} this
31847          * @param {Object} file
31848          */
31849         "previewrendered" : true,
31850         /**
31851          */
31852         "previewResize" : true
31853         
31854     });
31855 };
31856
31857 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31858     
31859     boxes : 0,
31860     inputName : '',
31861     thumbSize : 300,
31862     multiple : true,
31863     files : false,
31864     method : 'POST',
31865     url : '',
31866     paramName : 'imageUpload',
31867     toolTipName : 'filename',
31868     fieldLabel : '',
31869     labelWidth : 4,
31870     labelAlign : 'left',
31871     editable : true,
31872     delegates : false,
31873     xhr : false, 
31874     
31875     labellg : 0,
31876     labelmd : 0,
31877     labelsm : 0,
31878     labelxs : 0,
31879     
31880     getAutoCreate : function()
31881     {   
31882         var managerWidget = {
31883             tag : 'div',
31884             cls : 'roo-document-manager',
31885             cn : [
31886                 {
31887                     tag : 'input',
31888                     cls : 'roo-document-manager-selector',
31889                     type : 'file'
31890                 },
31891                 {
31892                     tag : 'div',
31893                     cls : 'roo-document-manager-uploader',
31894                     cn : [
31895                         {
31896                             tag : 'div',
31897                             cls : 'roo-document-manager-upload-btn',
31898                             html : '<i class="fa fa-plus"></i>'
31899                         }
31900                     ]
31901                     
31902                 }
31903             ]
31904         };
31905         
31906         var content = [
31907             {
31908                 tag : 'div',
31909                 cls : 'column col-md-12',
31910                 cn : managerWidget
31911             }
31912         ];
31913         
31914         if(this.fieldLabel.length){
31915             
31916             content = [
31917                 {
31918                     tag : 'div',
31919                     cls : 'column col-md-12',
31920                     html : this.fieldLabel
31921                 },
31922                 {
31923                     tag : 'div',
31924                     cls : 'column col-md-12',
31925                     cn : managerWidget
31926                 }
31927             ];
31928
31929             if(this.labelAlign == 'left'){
31930                 content = [
31931                     {
31932                         tag : 'div',
31933                         cls : 'column',
31934                         html : this.fieldLabel
31935                     },
31936                     {
31937                         tag : 'div',
31938                         cls : 'column',
31939                         cn : managerWidget
31940                     }
31941                 ];
31942                 
31943                 if(this.labelWidth > 12){
31944                     content[0].style = "width: " + this.labelWidth + 'px';
31945                 }
31946
31947                 if(this.labelWidth < 13 && this.labelmd == 0){
31948                     this.labelmd = this.labelWidth;
31949                 }
31950
31951                 if(this.labellg > 0){
31952                     content[0].cls += ' col-lg-' + this.labellg;
31953                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31954                 }
31955
31956                 if(this.labelmd > 0){
31957                     content[0].cls += ' col-md-' + this.labelmd;
31958                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31959                 }
31960
31961                 if(this.labelsm > 0){
31962                     content[0].cls += ' col-sm-' + this.labelsm;
31963                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31964                 }
31965
31966                 if(this.labelxs > 0){
31967                     content[0].cls += ' col-xs-' + this.labelxs;
31968                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31969                 }
31970                 
31971             }
31972         }
31973         
31974         var cfg = {
31975             tag : 'div',
31976             cls : 'row clearfix',
31977             cn : content
31978         };
31979         
31980         return cfg;
31981         
31982     },
31983     
31984     initEvents : function()
31985     {
31986         this.managerEl = this.el.select('.roo-document-manager', true).first();
31987         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31988         
31989         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31990         this.selectorEl.hide();
31991         
31992         if(this.multiple){
31993             this.selectorEl.attr('multiple', 'multiple');
31994         }
31995         
31996         this.selectorEl.on('change', this.onFileSelected, this);
31997         
31998         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31999         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32000         
32001         this.uploader.on('click', this.onUploaderClick, this);
32002         
32003         this.renderProgressDialog();
32004         
32005         var _this = this;
32006         
32007         window.addEventListener("resize", function() { _this.refresh(); } );
32008         
32009         this.fireEvent('initial', this);
32010     },
32011     
32012     renderProgressDialog : function()
32013     {
32014         var _this = this;
32015         
32016         this.progressDialog = new Roo.bootstrap.Modal({
32017             cls : 'roo-document-manager-progress-dialog',
32018             allow_close : false,
32019             animate : false,
32020             title : '',
32021             buttons : [
32022                 {
32023                     name  :'cancel',
32024                     weight : 'danger',
32025                     html : 'Cancel'
32026                 }
32027             ], 
32028             listeners : { 
32029                 btnclick : function() {
32030                     _this.uploadCancel();
32031                     this.hide();
32032                 }
32033             }
32034         });
32035          
32036         this.progressDialog.render(Roo.get(document.body));
32037          
32038         this.progress = new Roo.bootstrap.Progress({
32039             cls : 'roo-document-manager-progress',
32040             active : true,
32041             striped : true
32042         });
32043         
32044         this.progress.render(this.progressDialog.getChildContainer());
32045         
32046         this.progressBar = new Roo.bootstrap.ProgressBar({
32047             cls : 'roo-document-manager-progress-bar',
32048             aria_valuenow : 0,
32049             aria_valuemin : 0,
32050             aria_valuemax : 12,
32051             panel : 'success'
32052         });
32053         
32054         this.progressBar.render(this.progress.getChildContainer());
32055     },
32056     
32057     onUploaderClick : function(e)
32058     {
32059         e.preventDefault();
32060      
32061         if(this.fireEvent('beforeselectfile', this) != false){
32062             this.selectorEl.dom.click();
32063         }
32064         
32065     },
32066     
32067     onFileSelected : function(e)
32068     {
32069         e.preventDefault();
32070         
32071         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32072             return;
32073         }
32074         
32075         Roo.each(this.selectorEl.dom.files, function(file){
32076             if(this.fireEvent('inspect', this, file) != false){
32077                 this.files.push(file);
32078             }
32079         }, this);
32080         
32081         this.queue();
32082         
32083     },
32084     
32085     queue : function()
32086     {
32087         this.selectorEl.dom.value = '';
32088         
32089         if(!this.files || !this.files.length){
32090             return;
32091         }
32092         
32093         if(this.boxes > 0 && this.files.length > this.boxes){
32094             this.files = this.files.slice(0, this.boxes);
32095         }
32096         
32097         this.uploader.show();
32098         
32099         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32100             this.uploader.hide();
32101         }
32102         
32103         var _this = this;
32104         
32105         var files = [];
32106         
32107         var docs = [];
32108         
32109         Roo.each(this.files, function(file){
32110             
32111             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32112                 var f = this.renderPreview(file);
32113                 files.push(f);
32114                 return;
32115             }
32116             
32117             if(file.type.indexOf('image') != -1){
32118                 this.delegates.push(
32119                     (function(){
32120                         _this.process(file);
32121                     }).createDelegate(this)
32122                 );
32123         
32124                 return;
32125             }
32126             
32127             docs.push(
32128                 (function(){
32129                     _this.process(file);
32130                 }).createDelegate(this)
32131             );
32132             
32133         }, this);
32134         
32135         this.files = files;
32136         
32137         this.delegates = this.delegates.concat(docs);
32138         
32139         if(!this.delegates.length){
32140             this.refresh();
32141             return;
32142         }
32143         
32144         this.progressBar.aria_valuemax = this.delegates.length;
32145         
32146         this.arrange();
32147         
32148         return;
32149     },
32150     
32151     arrange : function()
32152     {
32153         if(!this.delegates.length){
32154             this.progressDialog.hide();
32155             this.refresh();
32156             return;
32157         }
32158         
32159         var delegate = this.delegates.shift();
32160         
32161         this.progressDialog.show();
32162         
32163         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32164         
32165         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32166         
32167         delegate();
32168     },
32169     
32170     refresh : function()
32171     {
32172         this.uploader.show();
32173         
32174         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32175             this.uploader.hide();
32176         }
32177         
32178         Roo.isTouch ? this.closable(false) : this.closable(true);
32179         
32180         this.fireEvent('refresh', this);
32181     },
32182     
32183     onRemove : function(e, el, o)
32184     {
32185         e.preventDefault();
32186         
32187         this.fireEvent('remove', this, o);
32188         
32189     },
32190     
32191     remove : function(o)
32192     {
32193         var files = [];
32194         
32195         Roo.each(this.files, function(file){
32196             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32197                 files.push(file);
32198                 return;
32199             }
32200
32201             o.target.remove();
32202
32203         }, this);
32204         
32205         this.files = files;
32206         
32207         this.refresh();
32208     },
32209     
32210     clear : function()
32211     {
32212         Roo.each(this.files, function(file){
32213             if(!file.target){
32214                 return;
32215             }
32216             
32217             file.target.remove();
32218
32219         }, this);
32220         
32221         this.files = [];
32222         
32223         this.refresh();
32224     },
32225     
32226     onClick : function(e, el, o)
32227     {
32228         e.preventDefault();
32229         
32230         this.fireEvent('click', this, o);
32231         
32232     },
32233     
32234     closable : function(closable)
32235     {
32236         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32237             
32238             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32239             
32240             if(closable){
32241                 el.show();
32242                 return;
32243             }
32244             
32245             el.hide();
32246             
32247         }, this);
32248     },
32249     
32250     xhrOnLoad : function(xhr)
32251     {
32252         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32253             el.remove();
32254         }, this);
32255         
32256         if (xhr.readyState !== 4) {
32257             this.arrange();
32258             this.fireEvent('exception', this, xhr);
32259             return;
32260         }
32261
32262         var response = Roo.decode(xhr.responseText);
32263         
32264         if(!response.success){
32265             this.arrange();
32266             this.fireEvent('exception', this, xhr);
32267             return;
32268         }
32269         
32270         var file = this.renderPreview(response.data);
32271         
32272         this.files.push(file);
32273         
32274         this.arrange();
32275         
32276         this.fireEvent('afterupload', this, xhr);
32277         
32278     },
32279     
32280     xhrOnError : function(xhr)
32281     {
32282         Roo.log('xhr on error');
32283         
32284         var response = Roo.decode(xhr.responseText);
32285           
32286         Roo.log(response);
32287         
32288         this.arrange();
32289     },
32290     
32291     process : function(file)
32292     {
32293         if(this.fireEvent('process', this, file) !== false){
32294             if(this.editable && file.type.indexOf('image') != -1){
32295                 this.fireEvent('edit', this, file);
32296                 return;
32297             }
32298
32299             this.uploadStart(file, false);
32300
32301             return;
32302         }
32303         
32304     },
32305     
32306     uploadStart : function(file, crop)
32307     {
32308         this.xhr = new XMLHttpRequest();
32309         
32310         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32311             this.arrange();
32312             return;
32313         }
32314         
32315         file.xhr = this.xhr;
32316             
32317         this.managerEl.createChild({
32318             tag : 'div',
32319             cls : 'roo-document-manager-loading',
32320             cn : [
32321                 {
32322                     tag : 'div',
32323                     tooltip : file.name,
32324                     cls : 'roo-document-manager-thumb',
32325                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32326                 }
32327             ]
32328
32329         });
32330
32331         this.xhr.open(this.method, this.url, true);
32332         
32333         var headers = {
32334             "Accept": "application/json",
32335             "Cache-Control": "no-cache",
32336             "X-Requested-With": "XMLHttpRequest"
32337         };
32338         
32339         for (var headerName in headers) {
32340             var headerValue = headers[headerName];
32341             if (headerValue) {
32342                 this.xhr.setRequestHeader(headerName, headerValue);
32343             }
32344         }
32345         
32346         var _this = this;
32347         
32348         this.xhr.onload = function()
32349         {
32350             _this.xhrOnLoad(_this.xhr);
32351         }
32352         
32353         this.xhr.onerror = function()
32354         {
32355             _this.xhrOnError(_this.xhr);
32356         }
32357         
32358         var formData = new FormData();
32359
32360         formData.append('returnHTML', 'NO');
32361         
32362         if(crop){
32363             formData.append('crop', crop);
32364         }
32365         
32366         formData.append(this.paramName, file, file.name);
32367         
32368         var options = {
32369             file : file, 
32370             manually : false
32371         };
32372         
32373         if(this.fireEvent('prepare', this, formData, options) != false){
32374             
32375             if(options.manually){
32376                 return;
32377             }
32378             
32379             this.xhr.send(formData);
32380             return;
32381         };
32382         
32383         this.uploadCancel();
32384     },
32385     
32386     uploadCancel : function()
32387     {
32388         if (this.xhr) {
32389             this.xhr.abort();
32390         }
32391         
32392         this.delegates = [];
32393         
32394         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32395             el.remove();
32396         }, this);
32397         
32398         this.arrange();
32399     },
32400     
32401     renderPreview : function(file)
32402     {
32403         if(typeof(file.target) != 'undefined' && file.target){
32404             return file;
32405         }
32406         
32407         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32408         
32409         var previewEl = this.managerEl.createChild({
32410             tag : 'div',
32411             cls : 'roo-document-manager-preview',
32412             cn : [
32413                 {
32414                     tag : 'div',
32415                     tooltip : file[this.toolTipName],
32416                     cls : 'roo-document-manager-thumb',
32417                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32418                 },
32419                 {
32420                     tag : 'button',
32421                     cls : 'close',
32422                     html : '<i class="fa fa-times-circle"></i>'
32423                 }
32424             ]
32425         });
32426
32427         var close = previewEl.select('button.close', true).first();
32428
32429         close.on('click', this.onRemove, this, file);
32430
32431         file.target = previewEl;
32432
32433         var image = previewEl.select('img', true).first();
32434         
32435         var _this = this;
32436         
32437         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32438         
32439         image.on('click', this.onClick, this, file);
32440         
32441         this.fireEvent('previewrendered', this, file);
32442         
32443         return file;
32444         
32445     },
32446     
32447     onPreviewLoad : function(file, image)
32448     {
32449         if(typeof(file.target) == 'undefined' || !file.target){
32450             return;
32451         }
32452         
32453         var width = image.dom.naturalWidth || image.dom.width;
32454         var height = image.dom.naturalHeight || image.dom.height;
32455         
32456         if(!this.previewResize) {
32457             return;
32458         }
32459         
32460         if(width > height){
32461             file.target.addClass('wide');
32462             return;
32463         }
32464         
32465         file.target.addClass('tall');
32466         return;
32467         
32468     },
32469     
32470     uploadFromSource : function(file, crop)
32471     {
32472         this.xhr = new XMLHttpRequest();
32473         
32474         this.managerEl.createChild({
32475             tag : 'div',
32476             cls : 'roo-document-manager-loading',
32477             cn : [
32478                 {
32479                     tag : 'div',
32480                     tooltip : file.name,
32481                     cls : 'roo-document-manager-thumb',
32482                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32483                 }
32484             ]
32485
32486         });
32487
32488         this.xhr.open(this.method, this.url, true);
32489         
32490         var headers = {
32491             "Accept": "application/json",
32492             "Cache-Control": "no-cache",
32493             "X-Requested-With": "XMLHttpRequest"
32494         };
32495         
32496         for (var headerName in headers) {
32497             var headerValue = headers[headerName];
32498             if (headerValue) {
32499                 this.xhr.setRequestHeader(headerName, headerValue);
32500             }
32501         }
32502         
32503         var _this = this;
32504         
32505         this.xhr.onload = function()
32506         {
32507             _this.xhrOnLoad(_this.xhr);
32508         }
32509         
32510         this.xhr.onerror = function()
32511         {
32512             _this.xhrOnError(_this.xhr);
32513         }
32514         
32515         var formData = new FormData();
32516
32517         formData.append('returnHTML', 'NO');
32518         
32519         formData.append('crop', crop);
32520         
32521         if(typeof(file.filename) != 'undefined'){
32522             formData.append('filename', file.filename);
32523         }
32524         
32525         if(typeof(file.mimetype) != 'undefined'){
32526             formData.append('mimetype', file.mimetype);
32527         }
32528         
32529         Roo.log(formData);
32530         
32531         if(this.fireEvent('prepare', this, formData) != false){
32532             this.xhr.send(formData);
32533         };
32534     }
32535 });
32536
32537 /*
32538 * Licence: LGPL
32539 */
32540
32541 /**
32542  * @class Roo.bootstrap.DocumentViewer
32543  * @extends Roo.bootstrap.Component
32544  * Bootstrap DocumentViewer class
32545  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32546  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32547  * 
32548  * @constructor
32549  * Create a new DocumentViewer
32550  * @param {Object} config The config object
32551  */
32552
32553 Roo.bootstrap.DocumentViewer = function(config){
32554     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32555     
32556     this.addEvents({
32557         /**
32558          * @event initial
32559          * Fire after initEvent
32560          * @param {Roo.bootstrap.DocumentViewer} this
32561          */
32562         "initial" : true,
32563         /**
32564          * @event click
32565          * Fire after click
32566          * @param {Roo.bootstrap.DocumentViewer} this
32567          */
32568         "click" : true,
32569         /**
32570          * @event download
32571          * Fire after download button
32572          * @param {Roo.bootstrap.DocumentViewer} this
32573          */
32574         "download" : true,
32575         /**
32576          * @event trash
32577          * Fire after trash button
32578          * @param {Roo.bootstrap.DocumentViewer} this
32579          */
32580         "trash" : true
32581         
32582     });
32583 };
32584
32585 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32586     
32587     showDownload : true,
32588     
32589     showTrash : true,
32590     
32591     getAutoCreate : function()
32592     {
32593         var cfg = {
32594             tag : 'div',
32595             cls : 'roo-document-viewer',
32596             cn : [
32597                 {
32598                     tag : 'div',
32599                     cls : 'roo-document-viewer-body',
32600                     cn : [
32601                         {
32602                             tag : 'div',
32603                             cls : 'roo-document-viewer-thumb',
32604                             cn : [
32605                                 {
32606                                     tag : 'img',
32607                                     cls : 'roo-document-viewer-image'
32608                                 }
32609                             ]
32610                         }
32611                     ]
32612                 },
32613                 {
32614                     tag : 'div',
32615                     cls : 'roo-document-viewer-footer',
32616                     cn : {
32617                         tag : 'div',
32618                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32619                         cn : [
32620                             {
32621                                 tag : 'div',
32622                                 cls : 'btn-group roo-document-viewer-download',
32623                                 cn : [
32624                                     {
32625                                         tag : 'button',
32626                                         cls : 'btn btn-default',
32627                                         html : '<i class="fa fa-download"></i>'
32628                                     }
32629                                 ]
32630                             },
32631                             {
32632                                 tag : 'div',
32633                                 cls : 'btn-group roo-document-viewer-trash',
32634                                 cn : [
32635                                     {
32636                                         tag : 'button',
32637                                         cls : 'btn btn-default',
32638                                         html : '<i class="fa fa-trash"></i>'
32639                                     }
32640                                 ]
32641                             }
32642                         ]
32643                     }
32644                 }
32645             ]
32646         };
32647         
32648         return cfg;
32649     },
32650     
32651     initEvents : function()
32652     {
32653         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32654         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32655         
32656         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32657         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32658         
32659         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32660         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32661         
32662         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32663         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32664         
32665         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32666         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32667         
32668         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32669         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32670         
32671         this.bodyEl.on('click', this.onClick, this);
32672         this.downloadBtn.on('click', this.onDownload, this);
32673         this.trashBtn.on('click', this.onTrash, this);
32674         
32675         this.downloadBtn.hide();
32676         this.trashBtn.hide();
32677         
32678         if(this.showDownload){
32679             this.downloadBtn.show();
32680         }
32681         
32682         if(this.showTrash){
32683             this.trashBtn.show();
32684         }
32685         
32686         if(!this.showDownload && !this.showTrash) {
32687             this.footerEl.hide();
32688         }
32689         
32690     },
32691     
32692     initial : function()
32693     {
32694         this.fireEvent('initial', this);
32695         
32696     },
32697     
32698     onClick : function(e)
32699     {
32700         e.preventDefault();
32701         
32702         this.fireEvent('click', this);
32703     },
32704     
32705     onDownload : function(e)
32706     {
32707         e.preventDefault();
32708         
32709         this.fireEvent('download', this);
32710     },
32711     
32712     onTrash : function(e)
32713     {
32714         e.preventDefault();
32715         
32716         this.fireEvent('trash', this);
32717     }
32718     
32719 });
32720 /*
32721  * - LGPL
32722  *
32723  * nav progress bar
32724  * 
32725  */
32726
32727 /**
32728  * @class Roo.bootstrap.NavProgressBar
32729  * @extends Roo.bootstrap.Component
32730  * Bootstrap NavProgressBar class
32731  * 
32732  * @constructor
32733  * Create a new nav progress bar
32734  * @param {Object} config The config object
32735  */
32736
32737 Roo.bootstrap.NavProgressBar = function(config){
32738     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32739
32740     this.bullets = this.bullets || [];
32741    
32742 //    Roo.bootstrap.NavProgressBar.register(this);
32743      this.addEvents({
32744         /**
32745              * @event changed
32746              * Fires when the active item changes
32747              * @param {Roo.bootstrap.NavProgressBar} this
32748              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32749              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32750          */
32751         'changed': true
32752      });
32753     
32754 };
32755
32756 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32757     
32758     bullets : [],
32759     barItems : [],
32760     
32761     getAutoCreate : function()
32762     {
32763         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32764         
32765         cfg = {
32766             tag : 'div',
32767             cls : 'roo-navigation-bar-group',
32768             cn : [
32769                 {
32770                     tag : 'div',
32771                     cls : 'roo-navigation-top-bar'
32772                 },
32773                 {
32774                     tag : 'div',
32775                     cls : 'roo-navigation-bullets-bar',
32776                     cn : [
32777                         {
32778                             tag : 'ul',
32779                             cls : 'roo-navigation-bar'
32780                         }
32781                     ]
32782                 },
32783                 
32784                 {
32785                     tag : 'div',
32786                     cls : 'roo-navigation-bottom-bar'
32787                 }
32788             ]
32789             
32790         };
32791         
32792         return cfg;
32793         
32794     },
32795     
32796     initEvents: function() 
32797     {
32798         
32799     },
32800     
32801     onRender : function(ct, position) 
32802     {
32803         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32804         
32805         if(this.bullets.length){
32806             Roo.each(this.bullets, function(b){
32807                this.addItem(b);
32808             }, this);
32809         }
32810         
32811         this.format();
32812         
32813     },
32814     
32815     addItem : function(cfg)
32816     {
32817         var item = new Roo.bootstrap.NavProgressItem(cfg);
32818         
32819         item.parentId = this.id;
32820         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32821         
32822         if(cfg.html){
32823             var top = new Roo.bootstrap.Element({
32824                 tag : 'div',
32825                 cls : 'roo-navigation-bar-text'
32826             });
32827             
32828             var bottom = new Roo.bootstrap.Element({
32829                 tag : 'div',
32830                 cls : 'roo-navigation-bar-text'
32831             });
32832             
32833             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32834             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32835             
32836             var topText = new Roo.bootstrap.Element({
32837                 tag : 'span',
32838                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32839             });
32840             
32841             var bottomText = new Roo.bootstrap.Element({
32842                 tag : 'span',
32843                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32844             });
32845             
32846             topText.onRender(top.el, null);
32847             bottomText.onRender(bottom.el, null);
32848             
32849             item.topEl = top;
32850             item.bottomEl = bottom;
32851         }
32852         
32853         this.barItems.push(item);
32854         
32855         return item;
32856     },
32857     
32858     getActive : function()
32859     {
32860         var active = false;
32861         
32862         Roo.each(this.barItems, function(v){
32863             
32864             if (!v.isActive()) {
32865                 return;
32866             }
32867             
32868             active = v;
32869             return false;
32870             
32871         });
32872         
32873         return active;
32874     },
32875     
32876     setActiveItem : function(item)
32877     {
32878         var prev = false;
32879         
32880         Roo.each(this.barItems, function(v){
32881             if (v.rid == item.rid) {
32882                 return ;
32883             }
32884             
32885             if (v.isActive()) {
32886                 v.setActive(false);
32887                 prev = v;
32888             }
32889         });
32890
32891         item.setActive(true);
32892         
32893         this.fireEvent('changed', this, item, prev);
32894     },
32895     
32896     getBarItem: function(rid)
32897     {
32898         var ret = false;
32899         
32900         Roo.each(this.barItems, function(e) {
32901             if (e.rid != rid) {
32902                 return;
32903             }
32904             
32905             ret =  e;
32906             return false;
32907         });
32908         
32909         return ret;
32910     },
32911     
32912     indexOfItem : function(item)
32913     {
32914         var index = false;
32915         
32916         Roo.each(this.barItems, function(v, i){
32917             
32918             if (v.rid != item.rid) {
32919                 return;
32920             }
32921             
32922             index = i;
32923             return false
32924         });
32925         
32926         return index;
32927     },
32928     
32929     setActiveNext : function()
32930     {
32931         var i = this.indexOfItem(this.getActive());
32932         
32933         if (i > this.barItems.length) {
32934             return;
32935         }
32936         
32937         this.setActiveItem(this.barItems[i+1]);
32938     },
32939     
32940     setActivePrev : function()
32941     {
32942         var i = this.indexOfItem(this.getActive());
32943         
32944         if (i  < 1) {
32945             return;
32946         }
32947         
32948         this.setActiveItem(this.barItems[i-1]);
32949     },
32950     
32951     format : function()
32952     {
32953         if(!this.barItems.length){
32954             return;
32955         }
32956      
32957         var width = 100 / this.barItems.length;
32958         
32959         Roo.each(this.barItems, function(i){
32960             i.el.setStyle('width', width + '%');
32961             i.topEl.el.setStyle('width', width + '%');
32962             i.bottomEl.el.setStyle('width', width + '%');
32963         }, this);
32964         
32965     }
32966     
32967 });
32968 /*
32969  * - LGPL
32970  *
32971  * Nav Progress Item
32972  * 
32973  */
32974
32975 /**
32976  * @class Roo.bootstrap.NavProgressItem
32977  * @extends Roo.bootstrap.Component
32978  * Bootstrap NavProgressItem class
32979  * @cfg {String} rid the reference id
32980  * @cfg {Boolean} active (true|false) Is item active default false
32981  * @cfg {Boolean} disabled (true|false) Is item active default false
32982  * @cfg {String} html
32983  * @cfg {String} position (top|bottom) text position default bottom
32984  * @cfg {String} icon show icon instead of number
32985  * 
32986  * @constructor
32987  * Create a new NavProgressItem
32988  * @param {Object} config The config object
32989  */
32990 Roo.bootstrap.NavProgressItem = function(config){
32991     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32992     this.addEvents({
32993         // raw events
32994         /**
32995          * @event click
32996          * The raw click event for the entire grid.
32997          * @param {Roo.bootstrap.NavProgressItem} this
32998          * @param {Roo.EventObject} e
32999          */
33000         "click" : true
33001     });
33002    
33003 };
33004
33005 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33006     
33007     rid : '',
33008     active : false,
33009     disabled : false,
33010     html : '',
33011     position : 'bottom',
33012     icon : false,
33013     
33014     getAutoCreate : function()
33015     {
33016         var iconCls = 'roo-navigation-bar-item-icon';
33017         
33018         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33019         
33020         var cfg = {
33021             tag: 'li',
33022             cls: 'roo-navigation-bar-item',
33023             cn : [
33024                 {
33025                     tag : 'i',
33026                     cls : iconCls
33027                 }
33028             ]
33029         };
33030         
33031         if(this.active){
33032             cfg.cls += ' active';
33033         }
33034         if(this.disabled){
33035             cfg.cls += ' disabled';
33036         }
33037         
33038         return cfg;
33039     },
33040     
33041     disable : function()
33042     {
33043         this.setDisabled(true);
33044     },
33045     
33046     enable : function()
33047     {
33048         this.setDisabled(false);
33049     },
33050     
33051     initEvents: function() 
33052     {
33053         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33054         
33055         this.iconEl.on('click', this.onClick, this);
33056     },
33057     
33058     onClick : function(e)
33059     {
33060         e.preventDefault();
33061         
33062         if(this.disabled){
33063             return;
33064         }
33065         
33066         if(this.fireEvent('click', this, e) === false){
33067             return;
33068         };
33069         
33070         this.parent().setActiveItem(this);
33071     },
33072     
33073     isActive: function () 
33074     {
33075         return this.active;
33076     },
33077     
33078     setActive : function(state)
33079     {
33080         if(this.active == state){
33081             return;
33082         }
33083         
33084         this.active = state;
33085         
33086         if (state) {
33087             this.el.addClass('active');
33088             return;
33089         }
33090         
33091         this.el.removeClass('active');
33092         
33093         return;
33094     },
33095     
33096     setDisabled : function(state)
33097     {
33098         if(this.disabled == state){
33099             return;
33100         }
33101         
33102         this.disabled = state;
33103         
33104         if (state) {
33105             this.el.addClass('disabled');
33106             return;
33107         }
33108         
33109         this.el.removeClass('disabled');
33110     },
33111     
33112     tooltipEl : function()
33113     {
33114         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33115     }
33116 });
33117  
33118
33119  /*
33120  * - LGPL
33121  *
33122  * FieldLabel
33123  * 
33124  */
33125
33126 /**
33127  * @class Roo.bootstrap.FieldLabel
33128  * @extends Roo.bootstrap.Component
33129  * Bootstrap FieldLabel class
33130  * @cfg {String} html contents of the element
33131  * @cfg {String} tag tag of the element default label
33132  * @cfg {String} cls class of the element
33133  * @cfg {String} target label target 
33134  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33135  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33136  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33137  * @cfg {String} iconTooltip default "This field is required"
33138  * @cfg {String} indicatorpos (left|right) default left
33139  * 
33140  * @constructor
33141  * Create a new FieldLabel
33142  * @param {Object} config The config object
33143  */
33144
33145 Roo.bootstrap.FieldLabel = function(config){
33146     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33147     
33148     this.addEvents({
33149             /**
33150              * @event invalid
33151              * Fires after the field has been marked as invalid.
33152              * @param {Roo.form.FieldLabel} this
33153              * @param {String} msg The validation message
33154              */
33155             invalid : true,
33156             /**
33157              * @event valid
33158              * Fires after the field has been validated with no errors.
33159              * @param {Roo.form.FieldLabel} this
33160              */
33161             valid : true
33162         });
33163 };
33164
33165 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33166     
33167     tag: 'label',
33168     cls: '',
33169     html: '',
33170     target: '',
33171     allowBlank : true,
33172     invalidClass : 'has-warning',
33173     validClass : 'has-success',
33174     iconTooltip : 'This field is required',
33175     indicatorpos : 'left',
33176     
33177     getAutoCreate : function(){
33178         
33179         var cls = "";
33180         if (!this.allowBlank) {
33181             cls  = "visible";
33182         }
33183         
33184         var cfg = {
33185             tag : this.tag,
33186             cls : 'roo-bootstrap-field-label ' + this.cls,
33187             for : this.target,
33188             cn : [
33189                 {
33190                     tag : 'i',
33191                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33192                     tooltip : this.iconTooltip
33193                 },
33194                 {
33195                     tag : 'span',
33196                     html : this.html
33197                 }
33198             ] 
33199         };
33200         
33201         if(this.indicatorpos == 'right'){
33202             var cfg = {
33203                 tag : this.tag,
33204                 cls : 'roo-bootstrap-field-label ' + this.cls,
33205                 for : this.target,
33206                 cn : [
33207                     {
33208                         tag : 'span',
33209                         html : this.html
33210                     },
33211                     {
33212                         tag : 'i',
33213                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33214                         tooltip : this.iconTooltip
33215                     }
33216                 ] 
33217             };
33218         }
33219         
33220         return cfg;
33221     },
33222     
33223     initEvents: function() 
33224     {
33225         Roo.bootstrap.Element.superclass.initEvents.call(this);
33226         
33227         this.indicator = this.indicatorEl();
33228         
33229         if(this.indicator){
33230             this.indicator.removeClass('visible');
33231             this.indicator.addClass('invisible');
33232         }
33233         
33234         Roo.bootstrap.FieldLabel.register(this);
33235     },
33236     
33237     indicatorEl : function()
33238     {
33239         var indicator = this.el.select('i.roo-required-indicator',true).first();
33240         
33241         if(!indicator){
33242             return false;
33243         }
33244         
33245         return indicator;
33246         
33247     },
33248     
33249     /**
33250      * Mark this field as valid
33251      */
33252     markValid : function()
33253     {
33254         if(this.indicator){
33255             this.indicator.removeClass('visible');
33256             this.indicator.addClass('invisible');
33257         }
33258         if (Roo.bootstrap.version == 3) {
33259             this.el.removeClass(this.invalidClass);
33260             this.el.addClass(this.validClass);
33261         } else {
33262             this.el.removeClass('is-invalid');
33263             this.el.addClass('is-valid');
33264         }
33265         
33266         
33267         this.fireEvent('valid', this);
33268     },
33269     
33270     /**
33271      * Mark this field as invalid
33272      * @param {String} msg The validation message
33273      */
33274     markInvalid : function(msg)
33275     {
33276         if(this.indicator){
33277             this.indicator.removeClass('invisible');
33278             this.indicator.addClass('visible');
33279         }
33280           if (Roo.bootstrap.version == 3) {
33281             this.el.removeClass(this.validClass);
33282             this.el.addClass(this.invalidClass);
33283         } else {
33284             this.el.removeClass('is-valid');
33285             this.el.addClass('is-invalid');
33286         }
33287         
33288         
33289         this.fireEvent('invalid', this, msg);
33290     }
33291     
33292    
33293 });
33294
33295 Roo.apply(Roo.bootstrap.FieldLabel, {
33296     
33297     groups: {},
33298     
33299      /**
33300     * register a FieldLabel Group
33301     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33302     */
33303     register : function(label)
33304     {
33305         if(this.groups.hasOwnProperty(label.target)){
33306             return;
33307         }
33308      
33309         this.groups[label.target] = label;
33310         
33311     },
33312     /**
33313     * fetch a FieldLabel Group based on the target
33314     * @param {string} target
33315     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33316     */
33317     get: function(target) {
33318         if (typeof(this.groups[target]) == 'undefined') {
33319             return false;
33320         }
33321         
33322         return this.groups[target] ;
33323     }
33324 });
33325
33326  
33327
33328  /*
33329  * - LGPL
33330  *
33331  * page DateSplitField.
33332  * 
33333  */
33334
33335
33336 /**
33337  * @class Roo.bootstrap.DateSplitField
33338  * @extends Roo.bootstrap.Component
33339  * Bootstrap DateSplitField class
33340  * @cfg {string} fieldLabel - the label associated
33341  * @cfg {Number} labelWidth set the width of label (0-12)
33342  * @cfg {String} labelAlign (top|left)
33343  * @cfg {Boolean} dayAllowBlank (true|false) default false
33344  * @cfg {Boolean} monthAllowBlank (true|false) default false
33345  * @cfg {Boolean} yearAllowBlank (true|false) default false
33346  * @cfg {string} dayPlaceholder 
33347  * @cfg {string} monthPlaceholder
33348  * @cfg {string} yearPlaceholder
33349  * @cfg {string} dayFormat default 'd'
33350  * @cfg {string} monthFormat default 'm'
33351  * @cfg {string} yearFormat default 'Y'
33352  * @cfg {Number} labellg set the width of label (1-12)
33353  * @cfg {Number} labelmd set the width of label (1-12)
33354  * @cfg {Number} labelsm set the width of label (1-12)
33355  * @cfg {Number} labelxs set the width of label (1-12)
33356
33357  *     
33358  * @constructor
33359  * Create a new DateSplitField
33360  * @param {Object} config The config object
33361  */
33362
33363 Roo.bootstrap.DateSplitField = function(config){
33364     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33365     
33366     this.addEvents({
33367         // raw events
33368          /**
33369          * @event years
33370          * getting the data of years
33371          * @param {Roo.bootstrap.DateSplitField} this
33372          * @param {Object} years
33373          */
33374         "years" : true,
33375         /**
33376          * @event days
33377          * getting the data of days
33378          * @param {Roo.bootstrap.DateSplitField} this
33379          * @param {Object} days
33380          */
33381         "days" : true,
33382         /**
33383          * @event invalid
33384          * Fires after the field has been marked as invalid.
33385          * @param {Roo.form.Field} this
33386          * @param {String} msg The validation message
33387          */
33388         invalid : true,
33389        /**
33390          * @event valid
33391          * Fires after the field has been validated with no errors.
33392          * @param {Roo.form.Field} this
33393          */
33394         valid : true
33395     });
33396 };
33397
33398 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33399     
33400     fieldLabel : '',
33401     labelAlign : 'top',
33402     labelWidth : 3,
33403     dayAllowBlank : false,
33404     monthAllowBlank : false,
33405     yearAllowBlank : false,
33406     dayPlaceholder : '',
33407     monthPlaceholder : '',
33408     yearPlaceholder : '',
33409     dayFormat : 'd',
33410     monthFormat : 'm',
33411     yearFormat : 'Y',
33412     isFormField : true,
33413     labellg : 0,
33414     labelmd : 0,
33415     labelsm : 0,
33416     labelxs : 0,
33417     
33418     getAutoCreate : function()
33419     {
33420         var cfg = {
33421             tag : 'div',
33422             cls : 'row roo-date-split-field-group',
33423             cn : [
33424                 {
33425                     tag : 'input',
33426                     type : 'hidden',
33427                     cls : 'form-hidden-field roo-date-split-field-group-value',
33428                     name : this.name
33429                 }
33430             ]
33431         };
33432         
33433         var labelCls = 'col-md-12';
33434         var contentCls = 'col-md-4';
33435         
33436         if(this.fieldLabel){
33437             
33438             var label = {
33439                 tag : 'div',
33440                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33441                 cn : [
33442                     {
33443                         tag : 'label',
33444                         html : this.fieldLabel
33445                     }
33446                 ]
33447             };
33448             
33449             if(this.labelAlign == 'left'){
33450             
33451                 if(this.labelWidth > 12){
33452                     label.style = "width: " + this.labelWidth + 'px';
33453                 }
33454
33455                 if(this.labelWidth < 13 && this.labelmd == 0){
33456                     this.labelmd = this.labelWidth;
33457                 }
33458
33459                 if(this.labellg > 0){
33460                     labelCls = ' col-lg-' + this.labellg;
33461                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33462                 }
33463
33464                 if(this.labelmd > 0){
33465                     labelCls = ' col-md-' + this.labelmd;
33466                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33467                 }
33468
33469                 if(this.labelsm > 0){
33470                     labelCls = ' col-sm-' + this.labelsm;
33471                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33472                 }
33473
33474                 if(this.labelxs > 0){
33475                     labelCls = ' col-xs-' + this.labelxs;
33476                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33477                 }
33478             }
33479             
33480             label.cls += ' ' + labelCls;
33481             
33482             cfg.cn.push(label);
33483         }
33484         
33485         Roo.each(['day', 'month', 'year'], function(t){
33486             cfg.cn.push({
33487                 tag : 'div',
33488                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33489             });
33490         }, this);
33491         
33492         return cfg;
33493     },
33494     
33495     inputEl: function ()
33496     {
33497         return this.el.select('.roo-date-split-field-group-value', true).first();
33498     },
33499     
33500     onRender : function(ct, position) 
33501     {
33502         var _this = this;
33503         
33504         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33505         
33506         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33507         
33508         this.dayField = new Roo.bootstrap.ComboBox({
33509             allowBlank : this.dayAllowBlank,
33510             alwaysQuery : true,
33511             displayField : 'value',
33512             editable : false,
33513             fieldLabel : '',
33514             forceSelection : true,
33515             mode : 'local',
33516             placeholder : this.dayPlaceholder,
33517             selectOnFocus : true,
33518             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33519             triggerAction : 'all',
33520             typeAhead : true,
33521             valueField : 'value',
33522             store : new Roo.data.SimpleStore({
33523                 data : (function() {    
33524                     var days = [];
33525                     _this.fireEvent('days', _this, days);
33526                     return days;
33527                 })(),
33528                 fields : [ 'value' ]
33529             }),
33530             listeners : {
33531                 select : function (_self, record, index)
33532                 {
33533                     _this.setValue(_this.getValue());
33534                 }
33535             }
33536         });
33537
33538         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33539         
33540         this.monthField = new Roo.bootstrap.MonthField({
33541             after : '<i class=\"fa fa-calendar\"></i>',
33542             allowBlank : this.monthAllowBlank,
33543             placeholder : this.monthPlaceholder,
33544             readOnly : true,
33545             listeners : {
33546                 render : function (_self)
33547                 {
33548                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33549                         e.preventDefault();
33550                         _self.focus();
33551                     });
33552                 },
33553                 select : function (_self, oldvalue, newvalue)
33554                 {
33555                     _this.setValue(_this.getValue());
33556                 }
33557             }
33558         });
33559         
33560         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33561         
33562         this.yearField = new Roo.bootstrap.ComboBox({
33563             allowBlank : this.yearAllowBlank,
33564             alwaysQuery : true,
33565             displayField : 'value',
33566             editable : false,
33567             fieldLabel : '',
33568             forceSelection : true,
33569             mode : 'local',
33570             placeholder : this.yearPlaceholder,
33571             selectOnFocus : true,
33572             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33573             triggerAction : 'all',
33574             typeAhead : true,
33575             valueField : 'value',
33576             store : new Roo.data.SimpleStore({
33577                 data : (function() {
33578                     var years = [];
33579                     _this.fireEvent('years', _this, years);
33580                     return years;
33581                 })(),
33582                 fields : [ 'value' ]
33583             }),
33584             listeners : {
33585                 select : function (_self, record, index)
33586                 {
33587                     _this.setValue(_this.getValue());
33588                 }
33589             }
33590         });
33591
33592         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33593     },
33594     
33595     setValue : function(v, format)
33596     {
33597         this.inputEl.dom.value = v;
33598         
33599         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33600         
33601         var d = Date.parseDate(v, f);
33602         
33603         if(!d){
33604             this.validate();
33605             return;
33606         }
33607         
33608         this.setDay(d.format(this.dayFormat));
33609         this.setMonth(d.format(this.monthFormat));
33610         this.setYear(d.format(this.yearFormat));
33611         
33612         this.validate();
33613         
33614         return;
33615     },
33616     
33617     setDay : function(v)
33618     {
33619         this.dayField.setValue(v);
33620         this.inputEl.dom.value = this.getValue();
33621         this.validate();
33622         return;
33623     },
33624     
33625     setMonth : function(v)
33626     {
33627         this.monthField.setValue(v, true);
33628         this.inputEl.dom.value = this.getValue();
33629         this.validate();
33630         return;
33631     },
33632     
33633     setYear : function(v)
33634     {
33635         this.yearField.setValue(v);
33636         this.inputEl.dom.value = this.getValue();
33637         this.validate();
33638         return;
33639     },
33640     
33641     getDay : function()
33642     {
33643         return this.dayField.getValue();
33644     },
33645     
33646     getMonth : function()
33647     {
33648         return this.monthField.getValue();
33649     },
33650     
33651     getYear : function()
33652     {
33653         return this.yearField.getValue();
33654     },
33655     
33656     getValue : function()
33657     {
33658         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33659         
33660         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33661         
33662         return date;
33663     },
33664     
33665     reset : function()
33666     {
33667         this.setDay('');
33668         this.setMonth('');
33669         this.setYear('');
33670         this.inputEl.dom.value = '';
33671         this.validate();
33672         return;
33673     },
33674     
33675     validate : function()
33676     {
33677         var d = this.dayField.validate();
33678         var m = this.monthField.validate();
33679         var y = this.yearField.validate();
33680         
33681         var valid = true;
33682         
33683         if(
33684                 (!this.dayAllowBlank && !d) ||
33685                 (!this.monthAllowBlank && !m) ||
33686                 (!this.yearAllowBlank && !y)
33687         ){
33688             valid = false;
33689         }
33690         
33691         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33692             return valid;
33693         }
33694         
33695         if(valid){
33696             this.markValid();
33697             return valid;
33698         }
33699         
33700         this.markInvalid();
33701         
33702         return valid;
33703     },
33704     
33705     markValid : function()
33706     {
33707         
33708         var label = this.el.select('label', true).first();
33709         var icon = this.el.select('i.fa-star', true).first();
33710
33711         if(label && icon){
33712             icon.remove();
33713         }
33714         
33715         this.fireEvent('valid', this);
33716     },
33717     
33718      /**
33719      * Mark this field as invalid
33720      * @param {String} msg The validation message
33721      */
33722     markInvalid : function(msg)
33723     {
33724         
33725         var label = this.el.select('label', true).first();
33726         var icon = this.el.select('i.fa-star', true).first();
33727
33728         if(label && !icon){
33729             this.el.select('.roo-date-split-field-label', true).createChild({
33730                 tag : 'i',
33731                 cls : 'text-danger fa fa-lg fa-star',
33732                 tooltip : 'This field is required',
33733                 style : 'margin-right:5px;'
33734             }, label, true);
33735         }
33736         
33737         this.fireEvent('invalid', this, msg);
33738     },
33739     
33740     clearInvalid : function()
33741     {
33742         var label = this.el.select('label', true).first();
33743         var icon = this.el.select('i.fa-star', true).first();
33744
33745         if(label && icon){
33746             icon.remove();
33747         }
33748         
33749         this.fireEvent('valid', this);
33750     },
33751     
33752     getName: function()
33753     {
33754         return this.name;
33755     }
33756     
33757 });
33758
33759  /**
33760  *
33761  * This is based on 
33762  * http://masonry.desandro.com
33763  *
33764  * The idea is to render all the bricks based on vertical width...
33765  *
33766  * The original code extends 'outlayer' - we might need to use that....
33767  * 
33768  */
33769
33770
33771 /**
33772  * @class Roo.bootstrap.LayoutMasonry
33773  * @extends Roo.bootstrap.Component
33774  * Bootstrap Layout Masonry class
33775  * 
33776  * @constructor
33777  * Create a new Element
33778  * @param {Object} config The config object
33779  */
33780
33781 Roo.bootstrap.LayoutMasonry = function(config){
33782     
33783     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33784     
33785     this.bricks = [];
33786     
33787     Roo.bootstrap.LayoutMasonry.register(this);
33788     
33789     this.addEvents({
33790         // raw events
33791         /**
33792          * @event layout
33793          * Fire after layout the items
33794          * @param {Roo.bootstrap.LayoutMasonry} this
33795          * @param {Roo.EventObject} e
33796          */
33797         "layout" : true
33798     });
33799     
33800 };
33801
33802 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33803     
33804     /**
33805      * @cfg {Boolean} isLayoutInstant = no animation?
33806      */   
33807     isLayoutInstant : false, // needed?
33808    
33809     /**
33810      * @cfg {Number} boxWidth  width of the columns
33811      */   
33812     boxWidth : 450,
33813     
33814       /**
33815      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33816      */   
33817     boxHeight : 0,
33818     
33819     /**
33820      * @cfg {Number} padWidth padding below box..
33821      */   
33822     padWidth : 10, 
33823     
33824     /**
33825      * @cfg {Number} gutter gutter width..
33826      */   
33827     gutter : 10,
33828     
33829      /**
33830      * @cfg {Number} maxCols maximum number of columns
33831      */   
33832     
33833     maxCols: 0,
33834     
33835     /**
33836      * @cfg {Boolean} isAutoInitial defalut true
33837      */   
33838     isAutoInitial : true, 
33839     
33840     containerWidth: 0,
33841     
33842     /**
33843      * @cfg {Boolean} isHorizontal defalut false
33844      */   
33845     isHorizontal : false, 
33846
33847     currentSize : null,
33848     
33849     tag: 'div',
33850     
33851     cls: '',
33852     
33853     bricks: null, //CompositeElement
33854     
33855     cols : 1,
33856     
33857     _isLayoutInited : false,
33858     
33859 //    isAlternative : false, // only use for vertical layout...
33860     
33861     /**
33862      * @cfg {Number} alternativePadWidth padding below box..
33863      */   
33864     alternativePadWidth : 50,
33865     
33866     selectedBrick : [],
33867     
33868     getAutoCreate : function(){
33869         
33870         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33871         
33872         var cfg = {
33873             tag: this.tag,
33874             cls: 'blog-masonary-wrapper ' + this.cls,
33875             cn : {
33876                 cls : 'mas-boxes masonary'
33877             }
33878         };
33879         
33880         return cfg;
33881     },
33882     
33883     getChildContainer: function( )
33884     {
33885         if (this.boxesEl) {
33886             return this.boxesEl;
33887         }
33888         
33889         this.boxesEl = this.el.select('.mas-boxes').first();
33890         
33891         return this.boxesEl;
33892     },
33893     
33894     
33895     initEvents : function()
33896     {
33897         var _this = this;
33898         
33899         if(this.isAutoInitial){
33900             Roo.log('hook children rendered');
33901             this.on('childrenrendered', function() {
33902                 Roo.log('children rendered');
33903                 _this.initial();
33904             } ,this);
33905         }
33906     },
33907     
33908     initial : function()
33909     {
33910         this.selectedBrick = [];
33911         
33912         this.currentSize = this.el.getBox(true);
33913         
33914         Roo.EventManager.onWindowResize(this.resize, this); 
33915
33916         if(!this.isAutoInitial){
33917             this.layout();
33918             return;
33919         }
33920         
33921         this.layout();
33922         
33923         return;
33924         //this.layout.defer(500,this);
33925         
33926     },
33927     
33928     resize : function()
33929     {
33930         var cs = this.el.getBox(true);
33931         
33932         if (
33933                 this.currentSize.width == cs.width && 
33934                 this.currentSize.x == cs.x && 
33935                 this.currentSize.height == cs.height && 
33936                 this.currentSize.y == cs.y 
33937         ) {
33938             Roo.log("no change in with or X or Y");
33939             return;
33940         }
33941         
33942         this.currentSize = cs;
33943         
33944         this.layout();
33945         
33946     },
33947     
33948     layout : function()
33949     {   
33950         this._resetLayout();
33951         
33952         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33953         
33954         this.layoutItems( isInstant );
33955       
33956         this._isLayoutInited = true;
33957         
33958         this.fireEvent('layout', this);
33959         
33960     },
33961     
33962     _resetLayout : function()
33963     {
33964         if(this.isHorizontal){
33965             this.horizontalMeasureColumns();
33966             return;
33967         }
33968         
33969         this.verticalMeasureColumns();
33970         
33971     },
33972     
33973     verticalMeasureColumns : function()
33974     {
33975         this.getContainerWidth();
33976         
33977 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33978 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33979 //            return;
33980 //        }
33981         
33982         var boxWidth = this.boxWidth + this.padWidth;
33983         
33984         if(this.containerWidth < this.boxWidth){
33985             boxWidth = this.containerWidth
33986         }
33987         
33988         var containerWidth = this.containerWidth;
33989         
33990         var cols = Math.floor(containerWidth / boxWidth);
33991         
33992         this.cols = Math.max( cols, 1 );
33993         
33994         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33995         
33996         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33997         
33998         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33999         
34000         this.colWidth = boxWidth + avail - this.padWidth;
34001         
34002         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34003         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34004     },
34005     
34006     horizontalMeasureColumns : function()
34007     {
34008         this.getContainerWidth();
34009         
34010         var boxWidth = this.boxWidth;
34011         
34012         if(this.containerWidth < boxWidth){
34013             boxWidth = this.containerWidth;
34014         }
34015         
34016         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34017         
34018         this.el.setHeight(boxWidth);
34019         
34020     },
34021     
34022     getContainerWidth : function()
34023     {
34024         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34025     },
34026     
34027     layoutItems : function( isInstant )
34028     {
34029         Roo.log(this.bricks);
34030         
34031         var items = Roo.apply([], this.bricks);
34032         
34033         if(this.isHorizontal){
34034             this._horizontalLayoutItems( items , isInstant );
34035             return;
34036         }
34037         
34038 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34039 //            this._verticalAlternativeLayoutItems( items , isInstant );
34040 //            return;
34041 //        }
34042         
34043         this._verticalLayoutItems( items , isInstant );
34044         
34045     },
34046     
34047     _verticalLayoutItems : function ( items , isInstant)
34048     {
34049         if ( !items || !items.length ) {
34050             return;
34051         }
34052         
34053         var standard = [
34054             ['xs', 'xs', 'xs', 'tall'],
34055             ['xs', 'xs', 'tall'],
34056             ['xs', 'xs', 'sm'],
34057             ['xs', 'xs', 'xs'],
34058             ['xs', 'tall'],
34059             ['xs', 'sm'],
34060             ['xs', 'xs'],
34061             ['xs'],
34062             
34063             ['sm', 'xs', 'xs'],
34064             ['sm', 'xs'],
34065             ['sm'],
34066             
34067             ['tall', 'xs', 'xs', 'xs'],
34068             ['tall', 'xs', 'xs'],
34069             ['tall', 'xs'],
34070             ['tall']
34071             
34072         ];
34073         
34074         var queue = [];
34075         
34076         var boxes = [];
34077         
34078         var box = [];
34079         
34080         Roo.each(items, function(item, k){
34081             
34082             switch (item.size) {
34083                 // these layouts take up a full box,
34084                 case 'md' :
34085                 case 'md-left' :
34086                 case 'md-right' :
34087                 case 'wide' :
34088                     
34089                     if(box.length){
34090                         boxes.push(box);
34091                         box = [];
34092                     }
34093                     
34094                     boxes.push([item]);
34095                     
34096                     break;
34097                     
34098                 case 'xs' :
34099                 case 'sm' :
34100                 case 'tall' :
34101                     
34102                     box.push(item);
34103                     
34104                     break;
34105                 default :
34106                     break;
34107                     
34108             }
34109             
34110         }, this);
34111         
34112         if(box.length){
34113             boxes.push(box);
34114             box = [];
34115         }
34116         
34117         var filterPattern = function(box, length)
34118         {
34119             if(!box.length){
34120                 return;
34121             }
34122             
34123             var match = false;
34124             
34125             var pattern = box.slice(0, length);
34126             
34127             var format = [];
34128             
34129             Roo.each(pattern, function(i){
34130                 format.push(i.size);
34131             }, this);
34132             
34133             Roo.each(standard, function(s){
34134                 
34135                 if(String(s) != String(format)){
34136                     return;
34137                 }
34138                 
34139                 match = true;
34140                 return false;
34141                 
34142             }, this);
34143             
34144             if(!match && length == 1){
34145                 return;
34146             }
34147             
34148             if(!match){
34149                 filterPattern(box, length - 1);
34150                 return;
34151             }
34152                 
34153             queue.push(pattern);
34154
34155             box = box.slice(length, box.length);
34156
34157             filterPattern(box, 4);
34158
34159             return;
34160             
34161         }
34162         
34163         Roo.each(boxes, function(box, k){
34164             
34165             if(!box.length){
34166                 return;
34167             }
34168             
34169             if(box.length == 1){
34170                 queue.push(box);
34171                 return;
34172             }
34173             
34174             filterPattern(box, 4);
34175             
34176         }, this);
34177         
34178         this._processVerticalLayoutQueue( queue, isInstant );
34179         
34180     },
34181     
34182 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34183 //    {
34184 //        if ( !items || !items.length ) {
34185 //            return;
34186 //        }
34187 //
34188 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34189 //        
34190 //    },
34191     
34192     _horizontalLayoutItems : function ( items , isInstant)
34193     {
34194         if ( !items || !items.length || items.length < 3) {
34195             return;
34196         }
34197         
34198         items.reverse();
34199         
34200         var eItems = items.slice(0, 3);
34201         
34202         items = items.slice(3, items.length);
34203         
34204         var standard = [
34205             ['xs', 'xs', 'xs', 'wide'],
34206             ['xs', 'xs', 'wide'],
34207             ['xs', 'xs', 'sm'],
34208             ['xs', 'xs', 'xs'],
34209             ['xs', 'wide'],
34210             ['xs', 'sm'],
34211             ['xs', 'xs'],
34212             ['xs'],
34213             
34214             ['sm', 'xs', 'xs'],
34215             ['sm', 'xs'],
34216             ['sm'],
34217             
34218             ['wide', 'xs', 'xs', 'xs'],
34219             ['wide', 'xs', 'xs'],
34220             ['wide', 'xs'],
34221             ['wide'],
34222             
34223             ['wide-thin']
34224         ];
34225         
34226         var queue = [];
34227         
34228         var boxes = [];
34229         
34230         var box = [];
34231         
34232         Roo.each(items, function(item, k){
34233             
34234             switch (item.size) {
34235                 case 'md' :
34236                 case 'md-left' :
34237                 case 'md-right' :
34238                 case 'tall' :
34239                     
34240                     if(box.length){
34241                         boxes.push(box);
34242                         box = [];
34243                     }
34244                     
34245                     boxes.push([item]);
34246                     
34247                     break;
34248                     
34249                 case 'xs' :
34250                 case 'sm' :
34251                 case 'wide' :
34252                 case 'wide-thin' :
34253                     
34254                     box.push(item);
34255                     
34256                     break;
34257                 default :
34258                     break;
34259                     
34260             }
34261             
34262         }, this);
34263         
34264         if(box.length){
34265             boxes.push(box);
34266             box = [];
34267         }
34268         
34269         var filterPattern = function(box, length)
34270         {
34271             if(!box.length){
34272                 return;
34273             }
34274             
34275             var match = false;
34276             
34277             var pattern = box.slice(0, length);
34278             
34279             var format = [];
34280             
34281             Roo.each(pattern, function(i){
34282                 format.push(i.size);
34283             }, this);
34284             
34285             Roo.each(standard, function(s){
34286                 
34287                 if(String(s) != String(format)){
34288                     return;
34289                 }
34290                 
34291                 match = true;
34292                 return false;
34293                 
34294             }, this);
34295             
34296             if(!match && length == 1){
34297                 return;
34298             }
34299             
34300             if(!match){
34301                 filterPattern(box, length - 1);
34302                 return;
34303             }
34304                 
34305             queue.push(pattern);
34306
34307             box = box.slice(length, box.length);
34308
34309             filterPattern(box, 4);
34310
34311             return;
34312             
34313         }
34314         
34315         Roo.each(boxes, function(box, k){
34316             
34317             if(!box.length){
34318                 return;
34319             }
34320             
34321             if(box.length == 1){
34322                 queue.push(box);
34323                 return;
34324             }
34325             
34326             filterPattern(box, 4);
34327             
34328         }, this);
34329         
34330         
34331         var prune = [];
34332         
34333         var pos = this.el.getBox(true);
34334         
34335         var minX = pos.x;
34336         
34337         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34338         
34339         var hit_end = false;
34340         
34341         Roo.each(queue, function(box){
34342             
34343             if(hit_end){
34344                 
34345                 Roo.each(box, function(b){
34346                 
34347                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34348                     b.el.hide();
34349
34350                 }, this);
34351
34352                 return;
34353             }
34354             
34355             var mx = 0;
34356             
34357             Roo.each(box, function(b){
34358                 
34359                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34360                 b.el.show();
34361
34362                 mx = Math.max(mx, b.x);
34363                 
34364             }, this);
34365             
34366             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34367             
34368             if(maxX < minX){
34369                 
34370                 Roo.each(box, function(b){
34371                 
34372                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34373                     b.el.hide();
34374                     
34375                 }, this);
34376                 
34377                 hit_end = true;
34378                 
34379                 return;
34380             }
34381             
34382             prune.push(box);
34383             
34384         }, this);
34385         
34386         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34387     },
34388     
34389     /** Sets position of item in DOM
34390     * @param {Element} item
34391     * @param {Number} x - horizontal position
34392     * @param {Number} y - vertical position
34393     * @param {Boolean} isInstant - disables transitions
34394     */
34395     _processVerticalLayoutQueue : function( queue, isInstant )
34396     {
34397         var pos = this.el.getBox(true);
34398         var x = pos.x;
34399         var y = pos.y;
34400         var maxY = [];
34401         
34402         for (var i = 0; i < this.cols; i++){
34403             maxY[i] = pos.y;
34404         }
34405         
34406         Roo.each(queue, function(box, k){
34407             
34408             var col = k % this.cols;
34409             
34410             Roo.each(box, function(b,kk){
34411                 
34412                 b.el.position('absolute');
34413                 
34414                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34415                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34416                 
34417                 if(b.size == 'md-left' || b.size == 'md-right'){
34418                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34419                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34420                 }
34421                 
34422                 b.el.setWidth(width);
34423                 b.el.setHeight(height);
34424                 // iframe?
34425                 b.el.select('iframe',true).setSize(width,height);
34426                 
34427             }, this);
34428             
34429             for (var i = 0; i < this.cols; i++){
34430                 
34431                 if(maxY[i] < maxY[col]){
34432                     col = i;
34433                     continue;
34434                 }
34435                 
34436                 col = Math.min(col, i);
34437                 
34438             }
34439             
34440             x = pos.x + col * (this.colWidth + this.padWidth);
34441             
34442             y = maxY[col];
34443             
34444             var positions = [];
34445             
34446             switch (box.length){
34447                 case 1 :
34448                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34449                     break;
34450                 case 2 :
34451                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34452                     break;
34453                 case 3 :
34454                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34455                     break;
34456                 case 4 :
34457                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34458                     break;
34459                 default :
34460                     break;
34461             }
34462             
34463             Roo.each(box, function(b,kk){
34464                 
34465                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34466                 
34467                 var sz = b.el.getSize();
34468                 
34469                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34470                 
34471             }, this);
34472             
34473         }, this);
34474         
34475         var mY = 0;
34476         
34477         for (var i = 0; i < this.cols; i++){
34478             mY = Math.max(mY, maxY[i]);
34479         }
34480         
34481         this.el.setHeight(mY - pos.y);
34482         
34483     },
34484     
34485 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34486 //    {
34487 //        var pos = this.el.getBox(true);
34488 //        var x = pos.x;
34489 //        var y = pos.y;
34490 //        var maxX = pos.right;
34491 //        
34492 //        var maxHeight = 0;
34493 //        
34494 //        Roo.each(items, function(item, k){
34495 //            
34496 //            var c = k % 2;
34497 //            
34498 //            item.el.position('absolute');
34499 //                
34500 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34501 //
34502 //            item.el.setWidth(width);
34503 //
34504 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34505 //
34506 //            item.el.setHeight(height);
34507 //            
34508 //            if(c == 0){
34509 //                item.el.setXY([x, y], isInstant ? false : true);
34510 //            } else {
34511 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34512 //            }
34513 //            
34514 //            y = y + height + this.alternativePadWidth;
34515 //            
34516 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34517 //            
34518 //        }, this);
34519 //        
34520 //        this.el.setHeight(maxHeight);
34521 //        
34522 //    },
34523     
34524     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34525     {
34526         var pos = this.el.getBox(true);
34527         
34528         var minX = pos.x;
34529         var minY = pos.y;
34530         
34531         var maxX = pos.right;
34532         
34533         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34534         
34535         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34536         
34537         Roo.each(queue, function(box, k){
34538             
34539             Roo.each(box, function(b, kk){
34540                 
34541                 b.el.position('absolute');
34542                 
34543                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34544                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34545                 
34546                 if(b.size == 'md-left' || b.size == 'md-right'){
34547                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34548                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34549                 }
34550                 
34551                 b.el.setWidth(width);
34552                 b.el.setHeight(height);
34553                 
34554             }, this);
34555             
34556             if(!box.length){
34557                 return;
34558             }
34559             
34560             var positions = [];
34561             
34562             switch (box.length){
34563                 case 1 :
34564                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34565                     break;
34566                 case 2 :
34567                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34568                     break;
34569                 case 3 :
34570                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34571                     break;
34572                 case 4 :
34573                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34574                     break;
34575                 default :
34576                     break;
34577             }
34578             
34579             Roo.each(box, function(b,kk){
34580                 
34581                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34582                 
34583                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34584                 
34585             }, this);
34586             
34587         }, this);
34588         
34589     },
34590     
34591     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34592     {
34593         Roo.each(eItems, function(b,k){
34594             
34595             b.size = (k == 0) ? 'sm' : 'xs';
34596             b.x = (k == 0) ? 2 : 1;
34597             b.y = (k == 0) ? 2 : 1;
34598             
34599             b.el.position('absolute');
34600             
34601             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34602                 
34603             b.el.setWidth(width);
34604             
34605             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34606             
34607             b.el.setHeight(height);
34608             
34609         }, this);
34610
34611         var positions = [];
34612         
34613         positions.push({
34614             x : maxX - this.unitWidth * 2 - this.gutter,
34615             y : minY
34616         });
34617         
34618         positions.push({
34619             x : maxX - this.unitWidth,
34620             y : minY + (this.unitWidth + this.gutter) * 2
34621         });
34622         
34623         positions.push({
34624             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34625             y : minY
34626         });
34627         
34628         Roo.each(eItems, function(b,k){
34629             
34630             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34631
34632         }, this);
34633         
34634     },
34635     
34636     getVerticalOneBoxColPositions : function(x, y, box)
34637     {
34638         var pos = [];
34639         
34640         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34641         
34642         if(box[0].size == 'md-left'){
34643             rand = 0;
34644         }
34645         
34646         if(box[0].size == 'md-right'){
34647             rand = 1;
34648         }
34649         
34650         pos.push({
34651             x : x + (this.unitWidth + this.gutter) * rand,
34652             y : y
34653         });
34654         
34655         return pos;
34656     },
34657     
34658     getVerticalTwoBoxColPositions : function(x, y, box)
34659     {
34660         var pos = [];
34661         
34662         if(box[0].size == 'xs'){
34663             
34664             pos.push({
34665                 x : x,
34666                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34667             });
34668
34669             pos.push({
34670                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34671                 y : y
34672             });
34673             
34674             return pos;
34675             
34676         }
34677         
34678         pos.push({
34679             x : x,
34680             y : y
34681         });
34682
34683         pos.push({
34684             x : x + (this.unitWidth + this.gutter) * 2,
34685             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34686         });
34687         
34688         return pos;
34689         
34690     },
34691     
34692     getVerticalThreeBoxColPositions : function(x, y, box)
34693     {
34694         var pos = [];
34695         
34696         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34697             
34698             pos.push({
34699                 x : x,
34700                 y : y
34701             });
34702
34703             pos.push({
34704                 x : x + (this.unitWidth + this.gutter) * 1,
34705                 y : y
34706             });
34707             
34708             pos.push({
34709                 x : x + (this.unitWidth + this.gutter) * 2,
34710                 y : y
34711             });
34712             
34713             return pos;
34714             
34715         }
34716         
34717         if(box[0].size == 'xs' && box[1].size == 'xs'){
34718             
34719             pos.push({
34720                 x : x,
34721                 y : y
34722             });
34723
34724             pos.push({
34725                 x : x,
34726                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34727             });
34728             
34729             pos.push({
34730                 x : x + (this.unitWidth + this.gutter) * 1,
34731                 y : y
34732             });
34733             
34734             return pos;
34735             
34736         }
34737         
34738         pos.push({
34739             x : x,
34740             y : y
34741         });
34742
34743         pos.push({
34744             x : x + (this.unitWidth + this.gutter) * 2,
34745             y : y
34746         });
34747
34748         pos.push({
34749             x : x + (this.unitWidth + this.gutter) * 2,
34750             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34751         });
34752             
34753         return pos;
34754         
34755     },
34756     
34757     getVerticalFourBoxColPositions : function(x, y, box)
34758     {
34759         var pos = [];
34760         
34761         if(box[0].size == 'xs'){
34762             
34763             pos.push({
34764                 x : x,
34765                 y : y
34766             });
34767
34768             pos.push({
34769                 x : x,
34770                 y : y + (this.unitHeight + this.gutter) * 1
34771             });
34772             
34773             pos.push({
34774                 x : x,
34775                 y : y + (this.unitHeight + this.gutter) * 2
34776             });
34777             
34778             pos.push({
34779                 x : x + (this.unitWidth + this.gutter) * 1,
34780                 y : y
34781             });
34782             
34783             return pos;
34784             
34785         }
34786         
34787         pos.push({
34788             x : x,
34789             y : y
34790         });
34791
34792         pos.push({
34793             x : x + (this.unitWidth + this.gutter) * 2,
34794             y : y
34795         });
34796
34797         pos.push({
34798             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34799             y : y + (this.unitHeight + this.gutter) * 1
34800         });
34801
34802         pos.push({
34803             x : x + (this.unitWidth + this.gutter) * 2,
34804             y : y + (this.unitWidth + this.gutter) * 2
34805         });
34806
34807         return pos;
34808         
34809     },
34810     
34811     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34812     {
34813         var pos = [];
34814         
34815         if(box[0].size == 'md-left'){
34816             pos.push({
34817                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34818                 y : minY
34819             });
34820             
34821             return pos;
34822         }
34823         
34824         if(box[0].size == 'md-right'){
34825             pos.push({
34826                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34827                 y : minY + (this.unitWidth + this.gutter) * 1
34828             });
34829             
34830             return pos;
34831         }
34832         
34833         var rand = Math.floor(Math.random() * (4 - box[0].y));
34834         
34835         pos.push({
34836             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34837             y : minY + (this.unitWidth + this.gutter) * rand
34838         });
34839         
34840         return pos;
34841         
34842     },
34843     
34844     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34845     {
34846         var pos = [];
34847         
34848         if(box[0].size == 'xs'){
34849             
34850             pos.push({
34851                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34852                 y : minY
34853             });
34854
34855             pos.push({
34856                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34857                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34858             });
34859             
34860             return pos;
34861             
34862         }
34863         
34864         pos.push({
34865             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34866             y : minY
34867         });
34868
34869         pos.push({
34870             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34871             y : minY + (this.unitWidth + this.gutter) * 2
34872         });
34873         
34874         return pos;
34875         
34876     },
34877     
34878     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34879     {
34880         var pos = [];
34881         
34882         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34883             
34884             pos.push({
34885                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34886                 y : minY
34887             });
34888
34889             pos.push({
34890                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34891                 y : minY + (this.unitWidth + this.gutter) * 1
34892             });
34893             
34894             pos.push({
34895                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34896                 y : minY + (this.unitWidth + this.gutter) * 2
34897             });
34898             
34899             return pos;
34900             
34901         }
34902         
34903         if(box[0].size == 'xs' && box[1].size == 'xs'){
34904             
34905             pos.push({
34906                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34907                 y : minY
34908             });
34909
34910             pos.push({
34911                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34912                 y : minY
34913             });
34914             
34915             pos.push({
34916                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34917                 y : minY + (this.unitWidth + this.gutter) * 1
34918             });
34919             
34920             return pos;
34921             
34922         }
34923         
34924         pos.push({
34925             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34926             y : minY
34927         });
34928
34929         pos.push({
34930             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34931             y : minY + (this.unitWidth + this.gutter) * 2
34932         });
34933
34934         pos.push({
34935             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34936             y : minY + (this.unitWidth + this.gutter) * 2
34937         });
34938             
34939         return pos;
34940         
34941     },
34942     
34943     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34944     {
34945         var pos = [];
34946         
34947         if(box[0].size == 'xs'){
34948             
34949             pos.push({
34950                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34951                 y : minY
34952             });
34953
34954             pos.push({
34955                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34956                 y : minY
34957             });
34958             
34959             pos.push({
34960                 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),
34961                 y : minY
34962             });
34963             
34964             pos.push({
34965                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34966                 y : minY + (this.unitWidth + this.gutter) * 1
34967             });
34968             
34969             return pos;
34970             
34971         }
34972         
34973         pos.push({
34974             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34975             y : minY
34976         });
34977         
34978         pos.push({
34979             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34980             y : minY + (this.unitWidth + this.gutter) * 2
34981         });
34982         
34983         pos.push({
34984             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34985             y : minY + (this.unitWidth + this.gutter) * 2
34986         });
34987         
34988         pos.push({
34989             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),
34990             y : minY + (this.unitWidth + this.gutter) * 2
34991         });
34992
34993         return pos;
34994         
34995     },
34996     
34997     /**
34998     * remove a Masonry Brick
34999     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35000     */
35001     removeBrick : function(brick_id)
35002     {
35003         if (!brick_id) {
35004             return;
35005         }
35006         
35007         for (var i = 0; i<this.bricks.length; i++) {
35008             if (this.bricks[i].id == brick_id) {
35009                 this.bricks.splice(i,1);
35010                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35011                 this.initial();
35012             }
35013         }
35014     },
35015     
35016     /**
35017     * adds a Masonry Brick
35018     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35019     */
35020     addBrick : function(cfg)
35021     {
35022         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35023         //this.register(cn);
35024         cn.parentId = this.id;
35025         cn.render(this.el);
35026         return cn;
35027     },
35028     
35029     /**
35030     * register a Masonry Brick
35031     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35032     */
35033     
35034     register : function(brick)
35035     {
35036         this.bricks.push(brick);
35037         brick.masonryId = this.id;
35038     },
35039     
35040     /**
35041     * clear all the Masonry Brick
35042     */
35043     clearAll : function()
35044     {
35045         this.bricks = [];
35046         //this.getChildContainer().dom.innerHTML = "";
35047         this.el.dom.innerHTML = '';
35048     },
35049     
35050     getSelected : function()
35051     {
35052         if (!this.selectedBrick) {
35053             return false;
35054         }
35055         
35056         return this.selectedBrick;
35057     }
35058 });
35059
35060 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35061     
35062     groups: {},
35063      /**
35064     * register a Masonry Layout
35065     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35066     */
35067     
35068     register : function(layout)
35069     {
35070         this.groups[layout.id] = layout;
35071     },
35072     /**
35073     * fetch a  Masonry Layout based on the masonry layout ID
35074     * @param {string} the masonry layout to add
35075     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35076     */
35077     
35078     get: function(layout_id) {
35079         if (typeof(this.groups[layout_id]) == 'undefined') {
35080             return false;
35081         }
35082         return this.groups[layout_id] ;
35083     }
35084     
35085     
35086     
35087 });
35088
35089  
35090
35091  /**
35092  *
35093  * This is based on 
35094  * http://masonry.desandro.com
35095  *
35096  * The idea is to render all the bricks based on vertical width...
35097  *
35098  * The original code extends 'outlayer' - we might need to use that....
35099  * 
35100  */
35101
35102
35103 /**
35104  * @class Roo.bootstrap.LayoutMasonryAuto
35105  * @extends Roo.bootstrap.Component
35106  * Bootstrap Layout Masonry class
35107  * 
35108  * @constructor
35109  * Create a new Element
35110  * @param {Object} config The config object
35111  */
35112
35113 Roo.bootstrap.LayoutMasonryAuto = function(config){
35114     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35115 };
35116
35117 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35118     
35119       /**
35120      * @cfg {Boolean} isFitWidth  - resize the width..
35121      */   
35122     isFitWidth : false,  // options..
35123     /**
35124      * @cfg {Boolean} isOriginLeft = left align?
35125      */   
35126     isOriginLeft : true,
35127     /**
35128      * @cfg {Boolean} isOriginTop = top align?
35129      */   
35130     isOriginTop : false,
35131     /**
35132      * @cfg {Boolean} isLayoutInstant = no animation?
35133      */   
35134     isLayoutInstant : false, // needed?
35135     /**
35136      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35137      */   
35138     isResizingContainer : true,
35139     /**
35140      * @cfg {Number} columnWidth  width of the columns 
35141      */   
35142     
35143     columnWidth : 0,
35144     
35145     /**
35146      * @cfg {Number} maxCols maximum number of columns
35147      */   
35148     
35149     maxCols: 0,
35150     /**
35151      * @cfg {Number} padHeight padding below box..
35152      */   
35153     
35154     padHeight : 10, 
35155     
35156     /**
35157      * @cfg {Boolean} isAutoInitial defalut true
35158      */   
35159     
35160     isAutoInitial : true, 
35161     
35162     // private?
35163     gutter : 0,
35164     
35165     containerWidth: 0,
35166     initialColumnWidth : 0,
35167     currentSize : null,
35168     
35169     colYs : null, // array.
35170     maxY : 0,
35171     padWidth: 10,
35172     
35173     
35174     tag: 'div',
35175     cls: '',
35176     bricks: null, //CompositeElement
35177     cols : 0, // array?
35178     // element : null, // wrapped now this.el
35179     _isLayoutInited : null, 
35180     
35181     
35182     getAutoCreate : function(){
35183         
35184         var cfg = {
35185             tag: this.tag,
35186             cls: 'blog-masonary-wrapper ' + this.cls,
35187             cn : {
35188                 cls : 'mas-boxes masonary'
35189             }
35190         };
35191         
35192         return cfg;
35193     },
35194     
35195     getChildContainer: function( )
35196     {
35197         if (this.boxesEl) {
35198             return this.boxesEl;
35199         }
35200         
35201         this.boxesEl = this.el.select('.mas-boxes').first();
35202         
35203         return this.boxesEl;
35204     },
35205     
35206     
35207     initEvents : function()
35208     {
35209         var _this = this;
35210         
35211         if(this.isAutoInitial){
35212             Roo.log('hook children rendered');
35213             this.on('childrenrendered', function() {
35214                 Roo.log('children rendered');
35215                 _this.initial();
35216             } ,this);
35217         }
35218         
35219     },
35220     
35221     initial : function()
35222     {
35223         this.reloadItems();
35224
35225         this.currentSize = this.el.getBox(true);
35226
35227         /// was window resize... - let's see if this works..
35228         Roo.EventManager.onWindowResize(this.resize, this); 
35229
35230         if(!this.isAutoInitial){
35231             this.layout();
35232             return;
35233         }
35234         
35235         this.layout.defer(500,this);
35236     },
35237     
35238     reloadItems: function()
35239     {
35240         this.bricks = this.el.select('.masonry-brick', true);
35241         
35242         this.bricks.each(function(b) {
35243             //Roo.log(b.getSize());
35244             if (!b.attr('originalwidth')) {
35245                 b.attr('originalwidth',  b.getSize().width);
35246             }
35247             
35248         });
35249         
35250         Roo.log(this.bricks.elements.length);
35251     },
35252     
35253     resize : function()
35254     {
35255         Roo.log('resize');
35256         var cs = this.el.getBox(true);
35257         
35258         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35259             Roo.log("no change in with or X");
35260             return;
35261         }
35262         this.currentSize = cs;
35263         this.layout();
35264     },
35265     
35266     layout : function()
35267     {
35268          Roo.log('layout');
35269         this._resetLayout();
35270         //this._manageStamps();
35271       
35272         // don't animate first layout
35273         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35274         this.layoutItems( isInstant );
35275       
35276         // flag for initalized
35277         this._isLayoutInited = true;
35278     },
35279     
35280     layoutItems : function( isInstant )
35281     {
35282         //var items = this._getItemsForLayout( this.items );
35283         // original code supports filtering layout items.. we just ignore it..
35284         
35285         this._layoutItems( this.bricks , isInstant );
35286       
35287         this._postLayout();
35288     },
35289     _layoutItems : function ( items , isInstant)
35290     {
35291        //this.fireEvent( 'layout', this, items );
35292     
35293
35294         if ( !items || !items.elements.length ) {
35295           // no items, emit event with empty array
35296             return;
35297         }
35298
35299         var queue = [];
35300         items.each(function(item) {
35301             Roo.log("layout item");
35302             Roo.log(item);
35303             // get x/y object from method
35304             var position = this._getItemLayoutPosition( item );
35305             // enqueue
35306             position.item = item;
35307             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35308             queue.push( position );
35309         }, this);
35310       
35311         this._processLayoutQueue( queue );
35312     },
35313     /** Sets position of item in DOM
35314     * @param {Element} item
35315     * @param {Number} x - horizontal position
35316     * @param {Number} y - vertical position
35317     * @param {Boolean} isInstant - disables transitions
35318     */
35319     _processLayoutQueue : function( queue )
35320     {
35321         for ( var i=0, len = queue.length; i < len; i++ ) {
35322             var obj = queue[i];
35323             obj.item.position('absolute');
35324             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35325         }
35326     },
35327       
35328     
35329     /**
35330     * Any logic you want to do after each layout,
35331     * i.e. size the container
35332     */
35333     _postLayout : function()
35334     {
35335         this.resizeContainer();
35336     },
35337     
35338     resizeContainer : function()
35339     {
35340         if ( !this.isResizingContainer ) {
35341             return;
35342         }
35343         var size = this._getContainerSize();
35344         if ( size ) {
35345             this.el.setSize(size.width,size.height);
35346             this.boxesEl.setSize(size.width,size.height);
35347         }
35348     },
35349     
35350     
35351     
35352     _resetLayout : function()
35353     {
35354         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35355         this.colWidth = this.el.getWidth();
35356         //this.gutter = this.el.getWidth(); 
35357         
35358         this.measureColumns();
35359
35360         // reset column Y
35361         var i = this.cols;
35362         this.colYs = [];
35363         while (i--) {
35364             this.colYs.push( 0 );
35365         }
35366     
35367         this.maxY = 0;
35368     },
35369
35370     measureColumns : function()
35371     {
35372         this.getContainerWidth();
35373       // if columnWidth is 0, default to outerWidth of first item
35374         if ( !this.columnWidth ) {
35375             var firstItem = this.bricks.first();
35376             Roo.log(firstItem);
35377             this.columnWidth  = this.containerWidth;
35378             if (firstItem && firstItem.attr('originalwidth') ) {
35379                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35380             }
35381             // columnWidth fall back to item of first element
35382             Roo.log("set column width?");
35383                         this.initialColumnWidth = this.columnWidth  ;
35384
35385             // if first elem has no width, default to size of container
35386             
35387         }
35388         
35389         
35390         if (this.initialColumnWidth) {
35391             this.columnWidth = this.initialColumnWidth;
35392         }
35393         
35394         
35395             
35396         // column width is fixed at the top - however if container width get's smaller we should
35397         // reduce it...
35398         
35399         // this bit calcs how man columns..
35400             
35401         var columnWidth = this.columnWidth += this.gutter;
35402       
35403         // calculate columns
35404         var containerWidth = this.containerWidth + this.gutter;
35405         
35406         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35407         // fix rounding errors, typically with gutters
35408         var excess = columnWidth - containerWidth % columnWidth;
35409         
35410         
35411         // if overshoot is less than a pixel, round up, otherwise floor it
35412         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35413         cols = Math[ mathMethod ]( cols );
35414         this.cols = Math.max( cols, 1 );
35415         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35416         
35417          // padding positioning..
35418         var totalColWidth = this.cols * this.columnWidth;
35419         var padavail = this.containerWidth - totalColWidth;
35420         // so for 2 columns - we need 3 'pads'
35421         
35422         var padNeeded = (1+this.cols) * this.padWidth;
35423         
35424         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35425         
35426         this.columnWidth += padExtra
35427         //this.padWidth = Math.floor(padavail /  ( this.cols));
35428         
35429         // adjust colum width so that padding is fixed??
35430         
35431         // we have 3 columns ... total = width * 3
35432         // we have X left over... that should be used by 
35433         
35434         //if (this.expandC) {
35435             
35436         //}
35437         
35438         
35439         
35440     },
35441     
35442     getContainerWidth : function()
35443     {
35444        /* // container is parent if fit width
35445         var container = this.isFitWidth ? this.element.parentNode : this.element;
35446         // check that this.size and size are there
35447         // IE8 triggers resize on body size change, so they might not be
35448         
35449         var size = getSize( container );  //FIXME
35450         this.containerWidth = size && size.innerWidth; //FIXME
35451         */
35452          
35453         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35454         
35455     },
35456     
35457     _getItemLayoutPosition : function( item )  // what is item?
35458     {
35459         // we resize the item to our columnWidth..
35460       
35461         item.setWidth(this.columnWidth);
35462         item.autoBoxAdjust  = false;
35463         
35464         var sz = item.getSize();
35465  
35466         // how many columns does this brick span
35467         var remainder = this.containerWidth % this.columnWidth;
35468         
35469         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35470         // round if off by 1 pixel, otherwise use ceil
35471         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35472         colSpan = Math.min( colSpan, this.cols );
35473         
35474         // normally this should be '1' as we dont' currently allow multi width columns..
35475         
35476         var colGroup = this._getColGroup( colSpan );
35477         // get the minimum Y value from the columns
35478         var minimumY = Math.min.apply( Math, colGroup );
35479         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35480         
35481         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35482          
35483         // position the brick
35484         var position = {
35485             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35486             y: this.currentSize.y + minimumY + this.padHeight
35487         };
35488         
35489         Roo.log(position);
35490         // apply setHeight to necessary columns
35491         var setHeight = minimumY + sz.height + this.padHeight;
35492         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35493         
35494         var setSpan = this.cols + 1 - colGroup.length;
35495         for ( var i = 0; i < setSpan; i++ ) {
35496           this.colYs[ shortColIndex + i ] = setHeight ;
35497         }
35498       
35499         return position;
35500     },
35501     
35502     /**
35503      * @param {Number} colSpan - number of columns the element spans
35504      * @returns {Array} colGroup
35505      */
35506     _getColGroup : function( colSpan )
35507     {
35508         if ( colSpan < 2 ) {
35509           // if brick spans only one column, use all the column Ys
35510           return this.colYs;
35511         }
35512       
35513         var colGroup = [];
35514         // how many different places could this brick fit horizontally
35515         var groupCount = this.cols + 1 - colSpan;
35516         // for each group potential horizontal position
35517         for ( var i = 0; i < groupCount; i++ ) {
35518           // make an array of colY values for that one group
35519           var groupColYs = this.colYs.slice( i, i + colSpan );
35520           // and get the max value of the array
35521           colGroup[i] = Math.max.apply( Math, groupColYs );
35522         }
35523         return colGroup;
35524     },
35525     /*
35526     _manageStamp : function( stamp )
35527     {
35528         var stampSize =  stamp.getSize();
35529         var offset = stamp.getBox();
35530         // get the columns that this stamp affects
35531         var firstX = this.isOriginLeft ? offset.x : offset.right;
35532         var lastX = firstX + stampSize.width;
35533         var firstCol = Math.floor( firstX / this.columnWidth );
35534         firstCol = Math.max( 0, firstCol );
35535         
35536         var lastCol = Math.floor( lastX / this.columnWidth );
35537         // lastCol should not go over if multiple of columnWidth #425
35538         lastCol -= lastX % this.columnWidth ? 0 : 1;
35539         lastCol = Math.min( this.cols - 1, lastCol );
35540         
35541         // set colYs to bottom of the stamp
35542         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35543             stampSize.height;
35544             
35545         for ( var i = firstCol; i <= lastCol; i++ ) {
35546           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35547         }
35548     },
35549     */
35550     
35551     _getContainerSize : function()
35552     {
35553         this.maxY = Math.max.apply( Math, this.colYs );
35554         var size = {
35555             height: this.maxY
35556         };
35557       
35558         if ( this.isFitWidth ) {
35559             size.width = this._getContainerFitWidth();
35560         }
35561       
35562         return size;
35563     },
35564     
35565     _getContainerFitWidth : function()
35566     {
35567         var unusedCols = 0;
35568         // count unused columns
35569         var i = this.cols;
35570         while ( --i ) {
35571           if ( this.colYs[i] !== 0 ) {
35572             break;
35573           }
35574           unusedCols++;
35575         }
35576         // fit container to columns that have been used
35577         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35578     },
35579     
35580     needsResizeLayout : function()
35581     {
35582         var previousWidth = this.containerWidth;
35583         this.getContainerWidth();
35584         return previousWidth !== this.containerWidth;
35585     }
35586  
35587 });
35588
35589  
35590
35591  /*
35592  * - LGPL
35593  *
35594  * element
35595  * 
35596  */
35597
35598 /**
35599  * @class Roo.bootstrap.MasonryBrick
35600  * @extends Roo.bootstrap.Component
35601  * Bootstrap MasonryBrick class
35602  * 
35603  * @constructor
35604  * Create a new MasonryBrick
35605  * @param {Object} config The config object
35606  */
35607
35608 Roo.bootstrap.MasonryBrick = function(config){
35609     
35610     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35611     
35612     Roo.bootstrap.MasonryBrick.register(this);
35613     
35614     this.addEvents({
35615         // raw events
35616         /**
35617          * @event click
35618          * When a MasonryBrick is clcik
35619          * @param {Roo.bootstrap.MasonryBrick} this
35620          * @param {Roo.EventObject} e
35621          */
35622         "click" : true
35623     });
35624 };
35625
35626 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35627     
35628     /**
35629      * @cfg {String} title
35630      */   
35631     title : '',
35632     /**
35633      * @cfg {String} html
35634      */   
35635     html : '',
35636     /**
35637      * @cfg {String} bgimage
35638      */   
35639     bgimage : '',
35640     /**
35641      * @cfg {String} videourl
35642      */   
35643     videourl : '',
35644     /**
35645      * @cfg {String} cls
35646      */   
35647     cls : '',
35648     /**
35649      * @cfg {String} href
35650      */   
35651     href : '',
35652     /**
35653      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35654      */   
35655     size : 'xs',
35656     
35657     /**
35658      * @cfg {String} placetitle (center|bottom)
35659      */   
35660     placetitle : '',
35661     
35662     /**
35663      * @cfg {Boolean} isFitContainer defalut true
35664      */   
35665     isFitContainer : true, 
35666     
35667     /**
35668      * @cfg {Boolean} preventDefault defalut false
35669      */   
35670     preventDefault : false, 
35671     
35672     /**
35673      * @cfg {Boolean} inverse defalut false
35674      */   
35675     maskInverse : false, 
35676     
35677     getAutoCreate : function()
35678     {
35679         if(!this.isFitContainer){
35680             return this.getSplitAutoCreate();
35681         }
35682         
35683         var cls = 'masonry-brick masonry-brick-full';
35684         
35685         if(this.href.length){
35686             cls += ' masonry-brick-link';
35687         }
35688         
35689         if(this.bgimage.length){
35690             cls += ' masonry-brick-image';
35691         }
35692         
35693         if(this.maskInverse){
35694             cls += ' mask-inverse';
35695         }
35696         
35697         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35698             cls += ' enable-mask';
35699         }
35700         
35701         if(this.size){
35702             cls += ' masonry-' + this.size + '-brick';
35703         }
35704         
35705         if(this.placetitle.length){
35706             
35707             switch (this.placetitle) {
35708                 case 'center' :
35709                     cls += ' masonry-center-title';
35710                     break;
35711                 case 'bottom' :
35712                     cls += ' masonry-bottom-title';
35713                     break;
35714                 default:
35715                     break;
35716             }
35717             
35718         } else {
35719             if(!this.html.length && !this.bgimage.length){
35720                 cls += ' masonry-center-title';
35721             }
35722
35723             if(!this.html.length && this.bgimage.length){
35724                 cls += ' masonry-bottom-title';
35725             }
35726         }
35727         
35728         if(this.cls){
35729             cls += ' ' + this.cls;
35730         }
35731         
35732         var cfg = {
35733             tag: (this.href.length) ? 'a' : 'div',
35734             cls: cls,
35735             cn: [
35736                 {
35737                     tag: 'div',
35738                     cls: 'masonry-brick-mask'
35739                 },
35740                 {
35741                     tag: 'div',
35742                     cls: 'masonry-brick-paragraph',
35743                     cn: []
35744                 }
35745             ]
35746         };
35747         
35748         if(this.href.length){
35749             cfg.href = this.href;
35750         }
35751         
35752         var cn = cfg.cn[1].cn;
35753         
35754         if(this.title.length){
35755             cn.push({
35756                 tag: 'h4',
35757                 cls: 'masonry-brick-title',
35758                 html: this.title
35759             });
35760         }
35761         
35762         if(this.html.length){
35763             cn.push({
35764                 tag: 'p',
35765                 cls: 'masonry-brick-text',
35766                 html: this.html
35767             });
35768         }
35769         
35770         if (!this.title.length && !this.html.length) {
35771             cfg.cn[1].cls += ' hide';
35772         }
35773         
35774         if(this.bgimage.length){
35775             cfg.cn.push({
35776                 tag: 'img',
35777                 cls: 'masonry-brick-image-view',
35778                 src: this.bgimage
35779             });
35780         }
35781         
35782         if(this.videourl.length){
35783             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35784             // youtube support only?
35785             cfg.cn.push({
35786                 tag: 'iframe',
35787                 cls: 'masonry-brick-image-view',
35788                 src: vurl,
35789                 frameborder : 0,
35790                 allowfullscreen : true
35791             });
35792         }
35793         
35794         return cfg;
35795         
35796     },
35797     
35798     getSplitAutoCreate : function()
35799     {
35800         var cls = 'masonry-brick masonry-brick-split';
35801         
35802         if(this.href.length){
35803             cls += ' masonry-brick-link';
35804         }
35805         
35806         if(this.bgimage.length){
35807             cls += ' masonry-brick-image';
35808         }
35809         
35810         if(this.size){
35811             cls += ' masonry-' + this.size + '-brick';
35812         }
35813         
35814         switch (this.placetitle) {
35815             case 'center' :
35816                 cls += ' masonry-center-title';
35817                 break;
35818             case 'bottom' :
35819                 cls += ' masonry-bottom-title';
35820                 break;
35821             default:
35822                 if(!this.bgimage.length){
35823                     cls += ' masonry-center-title';
35824                 }
35825
35826                 if(this.bgimage.length){
35827                     cls += ' masonry-bottom-title';
35828                 }
35829                 break;
35830         }
35831         
35832         if(this.cls){
35833             cls += ' ' + this.cls;
35834         }
35835         
35836         var cfg = {
35837             tag: (this.href.length) ? 'a' : 'div',
35838             cls: cls,
35839             cn: [
35840                 {
35841                     tag: 'div',
35842                     cls: 'masonry-brick-split-head',
35843                     cn: [
35844                         {
35845                             tag: 'div',
35846                             cls: 'masonry-brick-paragraph',
35847                             cn: []
35848                         }
35849                     ]
35850                 },
35851                 {
35852                     tag: 'div',
35853                     cls: 'masonry-brick-split-body',
35854                     cn: []
35855                 }
35856             ]
35857         };
35858         
35859         if(this.href.length){
35860             cfg.href = this.href;
35861         }
35862         
35863         if(this.title.length){
35864             cfg.cn[0].cn[0].cn.push({
35865                 tag: 'h4',
35866                 cls: 'masonry-brick-title',
35867                 html: this.title
35868             });
35869         }
35870         
35871         if(this.html.length){
35872             cfg.cn[1].cn.push({
35873                 tag: 'p',
35874                 cls: 'masonry-brick-text',
35875                 html: this.html
35876             });
35877         }
35878
35879         if(this.bgimage.length){
35880             cfg.cn[0].cn.push({
35881                 tag: 'img',
35882                 cls: 'masonry-brick-image-view',
35883                 src: this.bgimage
35884             });
35885         }
35886         
35887         if(this.videourl.length){
35888             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35889             // youtube support only?
35890             cfg.cn[0].cn.cn.push({
35891                 tag: 'iframe',
35892                 cls: 'masonry-brick-image-view',
35893                 src: vurl,
35894                 frameborder : 0,
35895                 allowfullscreen : true
35896             });
35897         }
35898         
35899         return cfg;
35900     },
35901     
35902     initEvents: function() 
35903     {
35904         switch (this.size) {
35905             case 'xs' :
35906                 this.x = 1;
35907                 this.y = 1;
35908                 break;
35909             case 'sm' :
35910                 this.x = 2;
35911                 this.y = 2;
35912                 break;
35913             case 'md' :
35914             case 'md-left' :
35915             case 'md-right' :
35916                 this.x = 3;
35917                 this.y = 3;
35918                 break;
35919             case 'tall' :
35920                 this.x = 2;
35921                 this.y = 3;
35922                 break;
35923             case 'wide' :
35924                 this.x = 3;
35925                 this.y = 2;
35926                 break;
35927             case 'wide-thin' :
35928                 this.x = 3;
35929                 this.y = 1;
35930                 break;
35931                         
35932             default :
35933                 break;
35934         }
35935         
35936         if(Roo.isTouch){
35937             this.el.on('touchstart', this.onTouchStart, this);
35938             this.el.on('touchmove', this.onTouchMove, this);
35939             this.el.on('touchend', this.onTouchEnd, this);
35940             this.el.on('contextmenu', this.onContextMenu, this);
35941         } else {
35942             this.el.on('mouseenter'  ,this.enter, this);
35943             this.el.on('mouseleave', this.leave, this);
35944             this.el.on('click', this.onClick, this);
35945         }
35946         
35947         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35948             this.parent().bricks.push(this);   
35949         }
35950         
35951     },
35952     
35953     onClick: function(e, el)
35954     {
35955         var time = this.endTimer - this.startTimer;
35956         // Roo.log(e.preventDefault());
35957         if(Roo.isTouch){
35958             if(time > 1000){
35959                 e.preventDefault();
35960                 return;
35961             }
35962         }
35963         
35964         if(!this.preventDefault){
35965             return;
35966         }
35967         
35968         e.preventDefault();
35969         
35970         if (this.activeClass != '') {
35971             this.selectBrick();
35972         }
35973         
35974         this.fireEvent('click', this, e);
35975     },
35976     
35977     enter: function(e, el)
35978     {
35979         e.preventDefault();
35980         
35981         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35982             return;
35983         }
35984         
35985         if(this.bgimage.length && this.html.length){
35986             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35987         }
35988     },
35989     
35990     leave: function(e, el)
35991     {
35992         e.preventDefault();
35993         
35994         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35995             return;
35996         }
35997         
35998         if(this.bgimage.length && this.html.length){
35999             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36000         }
36001     },
36002     
36003     onTouchStart: function(e, el)
36004     {
36005 //        e.preventDefault();
36006         
36007         this.touchmoved = false;
36008         
36009         if(!this.isFitContainer){
36010             return;
36011         }
36012         
36013         if(!this.bgimage.length || !this.html.length){
36014             return;
36015         }
36016         
36017         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36018         
36019         this.timer = new Date().getTime();
36020         
36021     },
36022     
36023     onTouchMove: function(e, el)
36024     {
36025         this.touchmoved = true;
36026     },
36027     
36028     onContextMenu : function(e,el)
36029     {
36030         e.preventDefault();
36031         e.stopPropagation();
36032         return false;
36033     },
36034     
36035     onTouchEnd: function(e, el)
36036     {
36037 //        e.preventDefault();
36038         
36039         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36040         
36041             this.leave(e,el);
36042             
36043             return;
36044         }
36045         
36046         if(!this.bgimage.length || !this.html.length){
36047             
36048             if(this.href.length){
36049                 window.location.href = this.href;
36050             }
36051             
36052             return;
36053         }
36054         
36055         if(!this.isFitContainer){
36056             return;
36057         }
36058         
36059         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36060         
36061         window.location.href = this.href;
36062     },
36063     
36064     //selection on single brick only
36065     selectBrick : function() {
36066         
36067         if (!this.parentId) {
36068             return;
36069         }
36070         
36071         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36072         var index = m.selectedBrick.indexOf(this.id);
36073         
36074         if ( index > -1) {
36075             m.selectedBrick.splice(index,1);
36076             this.el.removeClass(this.activeClass);
36077             return;
36078         }
36079         
36080         for(var i = 0; i < m.selectedBrick.length; i++) {
36081             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36082             b.el.removeClass(b.activeClass);
36083         }
36084         
36085         m.selectedBrick = [];
36086         
36087         m.selectedBrick.push(this.id);
36088         this.el.addClass(this.activeClass);
36089         return;
36090     },
36091     
36092     isSelected : function(){
36093         return this.el.hasClass(this.activeClass);
36094         
36095     }
36096 });
36097
36098 Roo.apply(Roo.bootstrap.MasonryBrick, {
36099     
36100     //groups: {},
36101     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36102      /**
36103     * register a Masonry Brick
36104     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36105     */
36106     
36107     register : function(brick)
36108     {
36109         //this.groups[brick.id] = brick;
36110         this.groups.add(brick.id, brick);
36111     },
36112     /**
36113     * fetch a  masonry brick based on the masonry brick ID
36114     * @param {string} the masonry brick to add
36115     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36116     */
36117     
36118     get: function(brick_id) 
36119     {
36120         // if (typeof(this.groups[brick_id]) == 'undefined') {
36121         //     return false;
36122         // }
36123         // return this.groups[brick_id] ;
36124         
36125         if(this.groups.key(brick_id)) {
36126             return this.groups.key(brick_id);
36127         }
36128         
36129         return false;
36130     }
36131     
36132     
36133     
36134 });
36135
36136  /*
36137  * - LGPL
36138  *
36139  * element
36140  * 
36141  */
36142
36143 /**
36144  * @class Roo.bootstrap.Brick
36145  * @extends Roo.bootstrap.Component
36146  * Bootstrap Brick class
36147  * 
36148  * @constructor
36149  * Create a new Brick
36150  * @param {Object} config The config object
36151  */
36152
36153 Roo.bootstrap.Brick = function(config){
36154     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36155     
36156     this.addEvents({
36157         // raw events
36158         /**
36159          * @event click
36160          * When a Brick is click
36161          * @param {Roo.bootstrap.Brick} this
36162          * @param {Roo.EventObject} e
36163          */
36164         "click" : true
36165     });
36166 };
36167
36168 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36169     
36170     /**
36171      * @cfg {String} title
36172      */   
36173     title : '',
36174     /**
36175      * @cfg {String} html
36176      */   
36177     html : '',
36178     /**
36179      * @cfg {String} bgimage
36180      */   
36181     bgimage : '',
36182     /**
36183      * @cfg {String} cls
36184      */   
36185     cls : '',
36186     /**
36187      * @cfg {String} href
36188      */   
36189     href : '',
36190     /**
36191      * @cfg {String} video
36192      */   
36193     video : '',
36194     /**
36195      * @cfg {Boolean} square
36196      */   
36197     square : true,
36198     
36199     getAutoCreate : function()
36200     {
36201         var cls = 'roo-brick';
36202         
36203         if(this.href.length){
36204             cls += ' roo-brick-link';
36205         }
36206         
36207         if(this.bgimage.length){
36208             cls += ' roo-brick-image';
36209         }
36210         
36211         if(!this.html.length && !this.bgimage.length){
36212             cls += ' roo-brick-center-title';
36213         }
36214         
36215         if(!this.html.length && this.bgimage.length){
36216             cls += ' roo-brick-bottom-title';
36217         }
36218         
36219         if(this.cls){
36220             cls += ' ' + this.cls;
36221         }
36222         
36223         var cfg = {
36224             tag: (this.href.length) ? 'a' : 'div',
36225             cls: cls,
36226             cn: [
36227                 {
36228                     tag: 'div',
36229                     cls: 'roo-brick-paragraph',
36230                     cn: []
36231                 }
36232             ]
36233         };
36234         
36235         if(this.href.length){
36236             cfg.href = this.href;
36237         }
36238         
36239         var cn = cfg.cn[0].cn;
36240         
36241         if(this.title.length){
36242             cn.push({
36243                 tag: 'h4',
36244                 cls: 'roo-brick-title',
36245                 html: this.title
36246             });
36247         }
36248         
36249         if(this.html.length){
36250             cn.push({
36251                 tag: 'p',
36252                 cls: 'roo-brick-text',
36253                 html: this.html
36254             });
36255         } else {
36256             cn.cls += ' hide';
36257         }
36258         
36259         if(this.bgimage.length){
36260             cfg.cn.push({
36261                 tag: 'img',
36262                 cls: 'roo-brick-image-view',
36263                 src: this.bgimage
36264             });
36265         }
36266         
36267         return cfg;
36268     },
36269     
36270     initEvents: function() 
36271     {
36272         if(this.title.length || this.html.length){
36273             this.el.on('mouseenter'  ,this.enter, this);
36274             this.el.on('mouseleave', this.leave, this);
36275         }
36276         
36277         Roo.EventManager.onWindowResize(this.resize, this); 
36278         
36279         if(this.bgimage.length){
36280             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36281             this.imageEl.on('load', this.onImageLoad, this);
36282             return;
36283         }
36284         
36285         this.resize();
36286     },
36287     
36288     onImageLoad : function()
36289     {
36290         this.resize();
36291     },
36292     
36293     resize : function()
36294     {
36295         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36296         
36297         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36298         
36299         if(this.bgimage.length){
36300             var image = this.el.select('.roo-brick-image-view', true).first();
36301             
36302             image.setWidth(paragraph.getWidth());
36303             
36304             if(this.square){
36305                 image.setHeight(paragraph.getWidth());
36306             }
36307             
36308             this.el.setHeight(image.getHeight());
36309             paragraph.setHeight(image.getHeight());
36310             
36311         }
36312         
36313     },
36314     
36315     enter: function(e, el)
36316     {
36317         e.preventDefault();
36318         
36319         if(this.bgimage.length){
36320             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36321             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36322         }
36323     },
36324     
36325     leave: function(e, el)
36326     {
36327         e.preventDefault();
36328         
36329         if(this.bgimage.length){
36330             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36331             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36332         }
36333     }
36334     
36335 });
36336
36337  
36338
36339  /*
36340  * - LGPL
36341  *
36342  * Number field 
36343  */
36344
36345 /**
36346  * @class Roo.bootstrap.NumberField
36347  * @extends Roo.bootstrap.Input
36348  * Bootstrap NumberField class
36349  * 
36350  * 
36351  * 
36352  * 
36353  * @constructor
36354  * Create a new NumberField
36355  * @param {Object} config The config object
36356  */
36357
36358 Roo.bootstrap.NumberField = function(config){
36359     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36360 };
36361
36362 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36363     
36364     /**
36365      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36366      */
36367     allowDecimals : true,
36368     /**
36369      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36370      */
36371     decimalSeparator : ".",
36372     /**
36373      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36374      */
36375     decimalPrecision : 2,
36376     /**
36377      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36378      */
36379     allowNegative : true,
36380     
36381     /**
36382      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36383      */
36384     allowZero: true,
36385     /**
36386      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36387      */
36388     minValue : Number.NEGATIVE_INFINITY,
36389     /**
36390      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36391      */
36392     maxValue : Number.MAX_VALUE,
36393     /**
36394      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36395      */
36396     minText : "The minimum value for this field is {0}",
36397     /**
36398      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36399      */
36400     maxText : "The maximum value for this field is {0}",
36401     /**
36402      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36403      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36404      */
36405     nanText : "{0} is not a valid number",
36406     /**
36407      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36408      */
36409     thousandsDelimiter : false,
36410     /**
36411      * @cfg {String} valueAlign alignment of value
36412      */
36413     valueAlign : "left",
36414
36415     getAutoCreate : function()
36416     {
36417         var hiddenInput = {
36418             tag: 'input',
36419             type: 'hidden',
36420             id: Roo.id(),
36421             cls: 'hidden-number-input'
36422         };
36423         
36424         if (this.name) {
36425             hiddenInput.name = this.name;
36426         }
36427         
36428         this.name = '';
36429         
36430         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36431         
36432         this.name = hiddenInput.name;
36433         
36434         if(cfg.cn.length > 0) {
36435             cfg.cn.push(hiddenInput);
36436         }
36437         
36438         return cfg;
36439     },
36440
36441     // private
36442     initEvents : function()
36443     {   
36444         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36445         
36446         var allowed = "0123456789";
36447         
36448         if(this.allowDecimals){
36449             allowed += this.decimalSeparator;
36450         }
36451         
36452         if(this.allowNegative){
36453             allowed += "-";
36454         }
36455         
36456         if(this.thousandsDelimiter) {
36457             allowed += ",";
36458         }
36459         
36460         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36461         
36462         var keyPress = function(e){
36463             
36464             var k = e.getKey();
36465             
36466             var c = e.getCharCode();
36467             
36468             if(
36469                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36470                     allowed.indexOf(String.fromCharCode(c)) === -1
36471             ){
36472                 e.stopEvent();
36473                 return;
36474             }
36475             
36476             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36477                 return;
36478             }
36479             
36480             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36481                 e.stopEvent();
36482             }
36483         };
36484         
36485         this.el.on("keypress", keyPress, this);
36486     },
36487     
36488     validateValue : function(value)
36489     {
36490         
36491         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36492             return false;
36493         }
36494         
36495         var num = this.parseValue(value);
36496         
36497         if(isNaN(num)){
36498             this.markInvalid(String.format(this.nanText, value));
36499             return false;
36500         }
36501         
36502         if(num < this.minValue){
36503             this.markInvalid(String.format(this.minText, this.minValue));
36504             return false;
36505         }
36506         
36507         if(num > this.maxValue){
36508             this.markInvalid(String.format(this.maxText, this.maxValue));
36509             return false;
36510         }
36511         
36512         return true;
36513     },
36514
36515     getValue : function()
36516     {
36517         var v = this.hiddenEl().getValue();
36518         
36519         return this.fixPrecision(this.parseValue(v));
36520     },
36521
36522     parseValue : function(value)
36523     {
36524         if(this.thousandsDelimiter) {
36525             value += "";
36526             r = new RegExp(",", "g");
36527             value = value.replace(r, "");
36528         }
36529         
36530         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36531         return isNaN(value) ? '' : value;
36532     },
36533
36534     fixPrecision : function(value)
36535     {
36536         if(this.thousandsDelimiter) {
36537             value += "";
36538             r = new RegExp(",", "g");
36539             value = value.replace(r, "");
36540         }
36541         
36542         var nan = isNaN(value);
36543         
36544         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36545             return nan ? '' : value;
36546         }
36547         return parseFloat(value).toFixed(this.decimalPrecision);
36548     },
36549
36550     setValue : function(v)
36551     {
36552         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36553         
36554         this.value = v;
36555         
36556         if(this.rendered){
36557             
36558             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36559             
36560             this.inputEl().dom.value = (v == '') ? '' :
36561                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36562             
36563             if(!this.allowZero && v === '0') {
36564                 this.hiddenEl().dom.value = '';
36565                 this.inputEl().dom.value = '';
36566             }
36567             
36568             this.validate();
36569         }
36570     },
36571
36572     decimalPrecisionFcn : function(v)
36573     {
36574         return Math.floor(v);
36575     },
36576
36577     beforeBlur : function()
36578     {
36579         var v = this.parseValue(this.getRawValue());
36580         
36581         if(v || v === 0 || v === ''){
36582             this.setValue(v);
36583         }
36584     },
36585     
36586     hiddenEl : function()
36587     {
36588         return this.el.select('input.hidden-number-input',true).first();
36589     }
36590     
36591 });
36592
36593  
36594
36595 /*
36596 * Licence: LGPL
36597 */
36598
36599 /**
36600  * @class Roo.bootstrap.DocumentSlider
36601  * @extends Roo.bootstrap.Component
36602  * Bootstrap DocumentSlider class
36603  * 
36604  * @constructor
36605  * Create a new DocumentViewer
36606  * @param {Object} config The config object
36607  */
36608
36609 Roo.bootstrap.DocumentSlider = function(config){
36610     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36611     
36612     this.files = [];
36613     
36614     this.addEvents({
36615         /**
36616          * @event initial
36617          * Fire after initEvent
36618          * @param {Roo.bootstrap.DocumentSlider} this
36619          */
36620         "initial" : true,
36621         /**
36622          * @event update
36623          * Fire after update
36624          * @param {Roo.bootstrap.DocumentSlider} this
36625          */
36626         "update" : true,
36627         /**
36628          * @event click
36629          * Fire after click
36630          * @param {Roo.bootstrap.DocumentSlider} this
36631          */
36632         "click" : true
36633     });
36634 };
36635
36636 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36637     
36638     files : false,
36639     
36640     indicator : 0,
36641     
36642     getAutoCreate : function()
36643     {
36644         var cfg = {
36645             tag : 'div',
36646             cls : 'roo-document-slider',
36647             cn : [
36648                 {
36649                     tag : 'div',
36650                     cls : 'roo-document-slider-header',
36651                     cn : [
36652                         {
36653                             tag : 'div',
36654                             cls : 'roo-document-slider-header-title'
36655                         }
36656                     ]
36657                 },
36658                 {
36659                     tag : 'div',
36660                     cls : 'roo-document-slider-body',
36661                     cn : [
36662                         {
36663                             tag : 'div',
36664                             cls : 'roo-document-slider-prev',
36665                             cn : [
36666                                 {
36667                                     tag : 'i',
36668                                     cls : 'fa fa-chevron-left'
36669                                 }
36670                             ]
36671                         },
36672                         {
36673                             tag : 'div',
36674                             cls : 'roo-document-slider-thumb',
36675                             cn : [
36676                                 {
36677                                     tag : 'img',
36678                                     cls : 'roo-document-slider-image'
36679                                 }
36680                             ]
36681                         },
36682                         {
36683                             tag : 'div',
36684                             cls : 'roo-document-slider-next',
36685                             cn : [
36686                                 {
36687                                     tag : 'i',
36688                                     cls : 'fa fa-chevron-right'
36689                                 }
36690                             ]
36691                         }
36692                     ]
36693                 }
36694             ]
36695         };
36696         
36697         return cfg;
36698     },
36699     
36700     initEvents : function()
36701     {
36702         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36703         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36704         
36705         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36706         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36707         
36708         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36709         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36710         
36711         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36712         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36713         
36714         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36715         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36716         
36717         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36718         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36719         
36720         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36721         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36722         
36723         this.thumbEl.on('click', this.onClick, this);
36724         
36725         this.prevIndicator.on('click', this.prev, this);
36726         
36727         this.nextIndicator.on('click', this.next, this);
36728         
36729     },
36730     
36731     initial : function()
36732     {
36733         if(this.files.length){
36734             this.indicator = 1;
36735             this.update()
36736         }
36737         
36738         this.fireEvent('initial', this);
36739     },
36740     
36741     update : function()
36742     {
36743         this.imageEl.attr('src', this.files[this.indicator - 1]);
36744         
36745         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36746         
36747         this.prevIndicator.show();
36748         
36749         if(this.indicator == 1){
36750             this.prevIndicator.hide();
36751         }
36752         
36753         this.nextIndicator.show();
36754         
36755         if(this.indicator == this.files.length){
36756             this.nextIndicator.hide();
36757         }
36758         
36759         this.thumbEl.scrollTo('top');
36760         
36761         this.fireEvent('update', this);
36762     },
36763     
36764     onClick : function(e)
36765     {
36766         e.preventDefault();
36767         
36768         this.fireEvent('click', this);
36769     },
36770     
36771     prev : function(e)
36772     {
36773         e.preventDefault();
36774         
36775         this.indicator = Math.max(1, this.indicator - 1);
36776         
36777         this.update();
36778     },
36779     
36780     next : function(e)
36781     {
36782         e.preventDefault();
36783         
36784         this.indicator = Math.min(this.files.length, this.indicator + 1);
36785         
36786         this.update();
36787     }
36788 });
36789 /*
36790  * - LGPL
36791  *
36792  * RadioSet
36793  *
36794  *
36795  */
36796
36797 /**
36798  * @class Roo.bootstrap.RadioSet
36799  * @extends Roo.bootstrap.Input
36800  * Bootstrap RadioSet class
36801  * @cfg {String} indicatorpos (left|right) default left
36802  * @cfg {Boolean} inline (true|false) inline the element (default true)
36803  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36804  * @constructor
36805  * Create a new RadioSet
36806  * @param {Object} config The config object
36807  */
36808
36809 Roo.bootstrap.RadioSet = function(config){
36810     
36811     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36812     
36813     this.radioes = [];
36814     
36815     Roo.bootstrap.RadioSet.register(this);
36816     
36817     this.addEvents({
36818         /**
36819         * @event check
36820         * Fires when the element is checked or unchecked.
36821         * @param {Roo.bootstrap.RadioSet} this This radio
36822         * @param {Roo.bootstrap.Radio} item The checked item
36823         */
36824        check : true,
36825        /**
36826         * @event click
36827         * Fires when the element is click.
36828         * @param {Roo.bootstrap.RadioSet} this This radio set
36829         * @param {Roo.bootstrap.Radio} item The checked item
36830         * @param {Roo.EventObject} e The event object
36831         */
36832        click : true
36833     });
36834     
36835 };
36836
36837 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36838
36839     radioes : false,
36840     
36841     inline : true,
36842     
36843     weight : '',
36844     
36845     indicatorpos : 'left',
36846     
36847     getAutoCreate : function()
36848     {
36849         var label = {
36850             tag : 'label',
36851             cls : 'roo-radio-set-label',
36852             cn : [
36853                 {
36854                     tag : 'span',
36855                     html : this.fieldLabel
36856                 }
36857             ]
36858         };
36859         if (Roo.bootstrap.version == 3) {
36860             
36861             
36862             if(this.indicatorpos == 'left'){
36863                 label.cn.unshift({
36864                     tag : 'i',
36865                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36866                     tooltip : 'This field is required'
36867                 });
36868             } else {
36869                 label.cn.push({
36870                     tag : 'i',
36871                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36872                     tooltip : 'This field is required'
36873                 });
36874             }
36875         }
36876         var items = {
36877             tag : 'div',
36878             cls : 'roo-radio-set-items'
36879         };
36880         
36881         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36882         
36883         if (align === 'left' && this.fieldLabel.length) {
36884             
36885             items = {
36886                 cls : "roo-radio-set-right", 
36887                 cn: [
36888                     items
36889                 ]
36890             };
36891             
36892             if(this.labelWidth > 12){
36893                 label.style = "width: " + this.labelWidth + 'px';
36894             }
36895             
36896             if(this.labelWidth < 13 && this.labelmd == 0){
36897                 this.labelmd = this.labelWidth;
36898             }
36899             
36900             if(this.labellg > 0){
36901                 label.cls += ' col-lg-' + this.labellg;
36902                 items.cls += ' col-lg-' + (12 - this.labellg);
36903             }
36904             
36905             if(this.labelmd > 0){
36906                 label.cls += ' col-md-' + this.labelmd;
36907                 items.cls += ' col-md-' + (12 - this.labelmd);
36908             }
36909             
36910             if(this.labelsm > 0){
36911                 label.cls += ' col-sm-' + this.labelsm;
36912                 items.cls += ' col-sm-' + (12 - this.labelsm);
36913             }
36914             
36915             if(this.labelxs > 0){
36916                 label.cls += ' col-xs-' + this.labelxs;
36917                 items.cls += ' col-xs-' + (12 - this.labelxs);
36918             }
36919         }
36920         
36921         var cfg = {
36922             tag : 'div',
36923             cls : 'roo-radio-set',
36924             cn : [
36925                 {
36926                     tag : 'input',
36927                     cls : 'roo-radio-set-input',
36928                     type : 'hidden',
36929                     name : this.name,
36930                     value : this.value ? this.value :  ''
36931                 },
36932                 label,
36933                 items
36934             ]
36935         };
36936         
36937         if(this.weight.length){
36938             cfg.cls += ' roo-radio-' + this.weight;
36939         }
36940         
36941         if(this.inline) {
36942             cfg.cls += ' roo-radio-set-inline';
36943         }
36944         
36945         var settings=this;
36946         ['xs','sm','md','lg'].map(function(size){
36947             if (settings[size]) {
36948                 cfg.cls += ' col-' + size + '-' + settings[size];
36949             }
36950         });
36951         
36952         return cfg;
36953         
36954     },
36955
36956     initEvents : function()
36957     {
36958         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36959         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36960         
36961         if(!this.fieldLabel.length){
36962             this.labelEl.hide();
36963         }
36964         
36965         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36966         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36967         
36968         this.indicator = this.indicatorEl();
36969         
36970         if(this.indicator){
36971             this.indicator.addClass('invisible');
36972         }
36973         
36974         this.originalValue = this.getValue();
36975         
36976     },
36977     
36978     inputEl: function ()
36979     {
36980         return this.el.select('.roo-radio-set-input', true).first();
36981     },
36982     
36983     getChildContainer : function()
36984     {
36985         return this.itemsEl;
36986     },
36987     
36988     register : function(item)
36989     {
36990         this.radioes.push(item);
36991         
36992     },
36993     
36994     validate : function()
36995     {   
36996         if(this.getVisibilityEl().hasClass('hidden')){
36997             return true;
36998         }
36999         
37000         var valid = false;
37001         
37002         Roo.each(this.radioes, function(i){
37003             if(!i.checked){
37004                 return;
37005             }
37006             
37007             valid = true;
37008             return false;
37009         });
37010         
37011         if(this.allowBlank) {
37012             return true;
37013         }
37014         
37015         if(this.disabled || valid){
37016             this.markValid();
37017             return true;
37018         }
37019         
37020         this.markInvalid();
37021         return false;
37022         
37023     },
37024     
37025     markValid : function()
37026     {
37027         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37028             this.indicatorEl().removeClass('visible');
37029             this.indicatorEl().addClass('invisible');
37030         }
37031         
37032         
37033         if (Roo.bootstrap.version == 3) {
37034             this.el.removeClass([this.invalidClass, this.validClass]);
37035             this.el.addClass(this.validClass);
37036         } else {
37037             this.el.removeClass(['is-invalid','is-valid']);
37038             this.el.addClass(['is-valid']);
37039         }
37040         this.fireEvent('valid', this);
37041     },
37042     
37043     markInvalid : function(msg)
37044     {
37045         if(this.allowBlank || this.disabled){
37046             return;
37047         }
37048         
37049         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37050             this.indicatorEl().removeClass('invisible');
37051             this.indicatorEl().addClass('visible');
37052         }
37053         if (Roo.bootstrap.version == 3) {
37054             this.el.removeClass([this.invalidClass, this.validClass]);
37055             this.el.addClass(this.invalidClass);
37056         } else {
37057             this.el.removeClass(['is-invalid','is-valid']);
37058             this.el.addClass(['is-invalid']);
37059         }
37060         
37061         this.fireEvent('invalid', this, msg);
37062         
37063     },
37064     
37065     setValue : function(v, suppressEvent)
37066     {   
37067         if(this.value === v){
37068             return;
37069         }
37070         
37071         this.value = v;
37072         
37073         if(this.rendered){
37074             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37075         }
37076         
37077         Roo.each(this.radioes, function(i){
37078             i.checked = false;
37079             i.el.removeClass('checked');
37080         });
37081         
37082         Roo.each(this.radioes, function(i){
37083             
37084             if(i.value === v || i.value.toString() === v.toString()){
37085                 i.checked = true;
37086                 i.el.addClass('checked');
37087                 
37088                 if(suppressEvent !== true){
37089                     this.fireEvent('check', this, i);
37090                 }
37091                 
37092                 return false;
37093             }
37094             
37095         }, this);
37096         
37097         this.validate();
37098     },
37099     
37100     clearInvalid : function(){
37101         
37102         if(!this.el || this.preventMark){
37103             return;
37104         }
37105         
37106         this.el.removeClass([this.invalidClass]);
37107         
37108         this.fireEvent('valid', this);
37109     }
37110     
37111 });
37112
37113 Roo.apply(Roo.bootstrap.RadioSet, {
37114     
37115     groups: {},
37116     
37117     register : function(set)
37118     {
37119         this.groups[set.name] = set;
37120     },
37121     
37122     get: function(name) 
37123     {
37124         if (typeof(this.groups[name]) == 'undefined') {
37125             return false;
37126         }
37127         
37128         return this.groups[name] ;
37129     }
37130     
37131 });
37132 /*
37133  * Based on:
37134  * Ext JS Library 1.1.1
37135  * Copyright(c) 2006-2007, Ext JS, LLC.
37136  *
37137  * Originally Released Under LGPL - original licence link has changed is not relivant.
37138  *
37139  * Fork - LGPL
37140  * <script type="text/javascript">
37141  */
37142
37143
37144 /**
37145  * @class Roo.bootstrap.SplitBar
37146  * @extends Roo.util.Observable
37147  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37148  * <br><br>
37149  * Usage:
37150  * <pre><code>
37151 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37152                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37153 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37154 split.minSize = 100;
37155 split.maxSize = 600;
37156 split.animate = true;
37157 split.on('moved', splitterMoved);
37158 </code></pre>
37159  * @constructor
37160  * Create a new SplitBar
37161  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37162  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37163  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37164  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37165                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37166                         position of the SplitBar).
37167  */
37168 Roo.bootstrap.SplitBar = function(cfg){
37169     
37170     /** @private */
37171     
37172     //{
37173     //  dragElement : elm
37174     //  resizingElement: el,
37175         // optional..
37176     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37177     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37178         // existingProxy ???
37179     //}
37180     
37181     this.el = Roo.get(cfg.dragElement, true);
37182     this.el.dom.unselectable = "on";
37183     /** @private */
37184     this.resizingEl = Roo.get(cfg.resizingElement, true);
37185
37186     /**
37187      * @private
37188      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37189      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37190      * @type Number
37191      */
37192     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37193     
37194     /**
37195      * The minimum size of the resizing element. (Defaults to 0)
37196      * @type Number
37197      */
37198     this.minSize = 0;
37199     
37200     /**
37201      * The maximum size of the resizing element. (Defaults to 2000)
37202      * @type Number
37203      */
37204     this.maxSize = 2000;
37205     
37206     /**
37207      * Whether to animate the transition to the new size
37208      * @type Boolean
37209      */
37210     this.animate = false;
37211     
37212     /**
37213      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37214      * @type Boolean
37215      */
37216     this.useShim = false;
37217     
37218     /** @private */
37219     this.shim = null;
37220     
37221     if(!cfg.existingProxy){
37222         /** @private */
37223         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37224     }else{
37225         this.proxy = Roo.get(cfg.existingProxy).dom;
37226     }
37227     /** @private */
37228     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37229     
37230     /** @private */
37231     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37232     
37233     /** @private */
37234     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37235     
37236     /** @private */
37237     this.dragSpecs = {};
37238     
37239     /**
37240      * @private The adapter to use to positon and resize elements
37241      */
37242     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37243     this.adapter.init(this);
37244     
37245     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37246         /** @private */
37247         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37248         this.el.addClass("roo-splitbar-h");
37249     }else{
37250         /** @private */
37251         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37252         this.el.addClass("roo-splitbar-v");
37253     }
37254     
37255     this.addEvents({
37256         /**
37257          * @event resize
37258          * Fires when the splitter is moved (alias for {@link #event-moved})
37259          * @param {Roo.bootstrap.SplitBar} this
37260          * @param {Number} newSize the new width or height
37261          */
37262         "resize" : true,
37263         /**
37264          * @event moved
37265          * Fires when the splitter is moved
37266          * @param {Roo.bootstrap.SplitBar} this
37267          * @param {Number} newSize the new width or height
37268          */
37269         "moved" : true,
37270         /**
37271          * @event beforeresize
37272          * Fires before the splitter is dragged
37273          * @param {Roo.bootstrap.SplitBar} this
37274          */
37275         "beforeresize" : true,
37276
37277         "beforeapply" : true
37278     });
37279
37280     Roo.util.Observable.call(this);
37281 };
37282
37283 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37284     onStartProxyDrag : function(x, y){
37285         this.fireEvent("beforeresize", this);
37286         if(!this.overlay){
37287             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37288             o.unselectable();
37289             o.enableDisplayMode("block");
37290             // all splitbars share the same overlay
37291             Roo.bootstrap.SplitBar.prototype.overlay = o;
37292         }
37293         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37294         this.overlay.show();
37295         Roo.get(this.proxy).setDisplayed("block");
37296         var size = this.adapter.getElementSize(this);
37297         this.activeMinSize = this.getMinimumSize();;
37298         this.activeMaxSize = this.getMaximumSize();;
37299         var c1 = size - this.activeMinSize;
37300         var c2 = Math.max(this.activeMaxSize - size, 0);
37301         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37302             this.dd.resetConstraints();
37303             this.dd.setXConstraint(
37304                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37305                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37306             );
37307             this.dd.setYConstraint(0, 0);
37308         }else{
37309             this.dd.resetConstraints();
37310             this.dd.setXConstraint(0, 0);
37311             this.dd.setYConstraint(
37312                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37313                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37314             );
37315          }
37316         this.dragSpecs.startSize = size;
37317         this.dragSpecs.startPoint = [x, y];
37318         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37319     },
37320     
37321     /** 
37322      * @private Called after the drag operation by the DDProxy
37323      */
37324     onEndProxyDrag : function(e){
37325         Roo.get(this.proxy).setDisplayed(false);
37326         var endPoint = Roo.lib.Event.getXY(e);
37327         if(this.overlay){
37328             this.overlay.hide();
37329         }
37330         var newSize;
37331         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37332             newSize = this.dragSpecs.startSize + 
37333                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37334                     endPoint[0] - this.dragSpecs.startPoint[0] :
37335                     this.dragSpecs.startPoint[0] - endPoint[0]
37336                 );
37337         }else{
37338             newSize = this.dragSpecs.startSize + 
37339                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37340                     endPoint[1] - this.dragSpecs.startPoint[1] :
37341                     this.dragSpecs.startPoint[1] - endPoint[1]
37342                 );
37343         }
37344         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37345         if(newSize != this.dragSpecs.startSize){
37346             if(this.fireEvent('beforeapply', this, newSize) !== false){
37347                 this.adapter.setElementSize(this, newSize);
37348                 this.fireEvent("moved", this, newSize);
37349                 this.fireEvent("resize", this, newSize);
37350             }
37351         }
37352     },
37353     
37354     /**
37355      * Get the adapter this SplitBar uses
37356      * @return The adapter object
37357      */
37358     getAdapter : function(){
37359         return this.adapter;
37360     },
37361     
37362     /**
37363      * Set the adapter this SplitBar uses
37364      * @param {Object} adapter A SplitBar adapter object
37365      */
37366     setAdapter : function(adapter){
37367         this.adapter = adapter;
37368         this.adapter.init(this);
37369     },
37370     
37371     /**
37372      * Gets the minimum size for the resizing element
37373      * @return {Number} The minimum size
37374      */
37375     getMinimumSize : function(){
37376         return this.minSize;
37377     },
37378     
37379     /**
37380      * Sets the minimum size for the resizing element
37381      * @param {Number} minSize The minimum size
37382      */
37383     setMinimumSize : function(minSize){
37384         this.minSize = minSize;
37385     },
37386     
37387     /**
37388      * Gets the maximum size for the resizing element
37389      * @return {Number} The maximum size
37390      */
37391     getMaximumSize : function(){
37392         return this.maxSize;
37393     },
37394     
37395     /**
37396      * Sets the maximum size for the resizing element
37397      * @param {Number} maxSize The maximum size
37398      */
37399     setMaximumSize : function(maxSize){
37400         this.maxSize = maxSize;
37401     },
37402     
37403     /**
37404      * Sets the initialize size for the resizing element
37405      * @param {Number} size The initial size
37406      */
37407     setCurrentSize : function(size){
37408         var oldAnimate = this.animate;
37409         this.animate = false;
37410         this.adapter.setElementSize(this, size);
37411         this.animate = oldAnimate;
37412     },
37413     
37414     /**
37415      * Destroy this splitbar. 
37416      * @param {Boolean} removeEl True to remove the element
37417      */
37418     destroy : function(removeEl){
37419         if(this.shim){
37420             this.shim.remove();
37421         }
37422         this.dd.unreg();
37423         this.proxy.parentNode.removeChild(this.proxy);
37424         if(removeEl){
37425             this.el.remove();
37426         }
37427     }
37428 });
37429
37430 /**
37431  * @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.
37432  */
37433 Roo.bootstrap.SplitBar.createProxy = function(dir){
37434     var proxy = new Roo.Element(document.createElement("div"));
37435     proxy.unselectable();
37436     var cls = 'roo-splitbar-proxy';
37437     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37438     document.body.appendChild(proxy.dom);
37439     return proxy.dom;
37440 };
37441
37442 /** 
37443  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37444  * Default Adapter. It assumes the splitter and resizing element are not positioned
37445  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37446  */
37447 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37448 };
37449
37450 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37451     // do nothing for now
37452     init : function(s){
37453     
37454     },
37455     /**
37456      * Called before drag operations to get the current size of the resizing element. 
37457      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37458      */
37459      getElementSize : function(s){
37460         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37461             return s.resizingEl.getWidth();
37462         }else{
37463             return s.resizingEl.getHeight();
37464         }
37465     },
37466     
37467     /**
37468      * Called after drag operations to set the size of the resizing element.
37469      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37470      * @param {Number} newSize The new size to set
37471      * @param {Function} onComplete A function to be invoked when resizing is complete
37472      */
37473     setElementSize : function(s, newSize, onComplete){
37474         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37475             if(!s.animate){
37476                 s.resizingEl.setWidth(newSize);
37477                 if(onComplete){
37478                     onComplete(s, newSize);
37479                 }
37480             }else{
37481                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37482             }
37483         }else{
37484             
37485             if(!s.animate){
37486                 s.resizingEl.setHeight(newSize);
37487                 if(onComplete){
37488                     onComplete(s, newSize);
37489                 }
37490             }else{
37491                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37492             }
37493         }
37494     }
37495 };
37496
37497 /** 
37498  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37499  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37500  * Adapter that  moves the splitter element to align with the resized sizing element. 
37501  * Used with an absolute positioned SplitBar.
37502  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37503  * document.body, make sure you assign an id to the body element.
37504  */
37505 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37506     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37507     this.container = Roo.get(container);
37508 };
37509
37510 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37511     init : function(s){
37512         this.basic.init(s);
37513     },
37514     
37515     getElementSize : function(s){
37516         return this.basic.getElementSize(s);
37517     },
37518     
37519     setElementSize : function(s, newSize, onComplete){
37520         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37521     },
37522     
37523     moveSplitter : function(s){
37524         var yes = Roo.bootstrap.SplitBar;
37525         switch(s.placement){
37526             case yes.LEFT:
37527                 s.el.setX(s.resizingEl.getRight());
37528                 break;
37529             case yes.RIGHT:
37530                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37531                 break;
37532             case yes.TOP:
37533                 s.el.setY(s.resizingEl.getBottom());
37534                 break;
37535             case yes.BOTTOM:
37536                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37537                 break;
37538         }
37539     }
37540 };
37541
37542 /**
37543  * Orientation constant - Create a vertical SplitBar
37544  * @static
37545  * @type Number
37546  */
37547 Roo.bootstrap.SplitBar.VERTICAL = 1;
37548
37549 /**
37550  * Orientation constant - Create a horizontal SplitBar
37551  * @static
37552  * @type Number
37553  */
37554 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37555
37556 /**
37557  * Placement constant - The resizing element is to the left of the splitter element
37558  * @static
37559  * @type Number
37560  */
37561 Roo.bootstrap.SplitBar.LEFT = 1;
37562
37563 /**
37564  * Placement constant - The resizing element is to the right of the splitter element
37565  * @static
37566  * @type Number
37567  */
37568 Roo.bootstrap.SplitBar.RIGHT = 2;
37569
37570 /**
37571  * Placement constant - The resizing element is positioned above the splitter element
37572  * @static
37573  * @type Number
37574  */
37575 Roo.bootstrap.SplitBar.TOP = 3;
37576
37577 /**
37578  * Placement constant - The resizing element is positioned under splitter element
37579  * @static
37580  * @type Number
37581  */
37582 Roo.bootstrap.SplitBar.BOTTOM = 4;
37583 Roo.namespace("Roo.bootstrap.layout");/*
37584  * Based on:
37585  * Ext JS Library 1.1.1
37586  * Copyright(c) 2006-2007, Ext JS, LLC.
37587  *
37588  * Originally Released Under LGPL - original licence link has changed is not relivant.
37589  *
37590  * Fork - LGPL
37591  * <script type="text/javascript">
37592  */
37593
37594 /**
37595  * @class Roo.bootstrap.layout.Manager
37596  * @extends Roo.bootstrap.Component
37597  * Base class for layout managers.
37598  */
37599 Roo.bootstrap.layout.Manager = function(config)
37600 {
37601     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37602
37603
37604
37605
37606
37607     /** false to disable window resize monitoring @type Boolean */
37608     this.monitorWindowResize = true;
37609     this.regions = {};
37610     this.addEvents({
37611         /**
37612          * @event layout
37613          * Fires when a layout is performed.
37614          * @param {Roo.LayoutManager} this
37615          */
37616         "layout" : true,
37617         /**
37618          * @event regionresized
37619          * Fires when the user resizes a region.
37620          * @param {Roo.LayoutRegion} region The resized region
37621          * @param {Number} newSize The new size (width for east/west, height for north/south)
37622          */
37623         "regionresized" : true,
37624         /**
37625          * @event regioncollapsed
37626          * Fires when a region is collapsed.
37627          * @param {Roo.LayoutRegion} region The collapsed region
37628          */
37629         "regioncollapsed" : true,
37630         /**
37631          * @event regionexpanded
37632          * Fires when a region is expanded.
37633          * @param {Roo.LayoutRegion} region The expanded region
37634          */
37635         "regionexpanded" : true
37636     });
37637     this.updating = false;
37638
37639     if (config.el) {
37640         this.el = Roo.get(config.el);
37641         this.initEvents();
37642     }
37643
37644 };
37645
37646 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37647
37648
37649     regions : null,
37650
37651     monitorWindowResize : true,
37652
37653
37654     updating : false,
37655
37656
37657     onRender : function(ct, position)
37658     {
37659         if(!this.el){
37660             this.el = Roo.get(ct);
37661             this.initEvents();
37662         }
37663         //this.fireEvent('render',this);
37664     },
37665
37666
37667     initEvents: function()
37668     {
37669
37670
37671         // ie scrollbar fix
37672         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37673             document.body.scroll = "no";
37674         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37675             this.el.position('relative');
37676         }
37677         this.id = this.el.id;
37678         this.el.addClass("roo-layout-container");
37679         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37680         if(this.el.dom != document.body ) {
37681             this.el.on('resize', this.layout,this);
37682             this.el.on('show', this.layout,this);
37683         }
37684
37685     },
37686
37687     /**
37688      * Returns true if this layout is currently being updated
37689      * @return {Boolean}
37690      */
37691     isUpdating : function(){
37692         return this.updating;
37693     },
37694
37695     /**
37696      * Suspend the LayoutManager from doing auto-layouts while
37697      * making multiple add or remove calls
37698      */
37699     beginUpdate : function(){
37700         this.updating = true;
37701     },
37702
37703     /**
37704      * Restore auto-layouts and optionally disable the manager from performing a layout
37705      * @param {Boolean} noLayout true to disable a layout update
37706      */
37707     endUpdate : function(noLayout){
37708         this.updating = false;
37709         if(!noLayout){
37710             this.layout();
37711         }
37712     },
37713
37714     layout: function(){
37715         // abstract...
37716     },
37717
37718     onRegionResized : function(region, newSize){
37719         this.fireEvent("regionresized", region, newSize);
37720         this.layout();
37721     },
37722
37723     onRegionCollapsed : function(region){
37724         this.fireEvent("regioncollapsed", region);
37725     },
37726
37727     onRegionExpanded : function(region){
37728         this.fireEvent("regionexpanded", region);
37729     },
37730
37731     /**
37732      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37733      * performs box-model adjustments.
37734      * @return {Object} The size as an object {width: (the width), height: (the height)}
37735      */
37736     getViewSize : function()
37737     {
37738         var size;
37739         if(this.el.dom != document.body){
37740             size = this.el.getSize();
37741         }else{
37742             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37743         }
37744         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37745         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37746         return size;
37747     },
37748
37749     /**
37750      * Returns the Element this layout is bound to.
37751      * @return {Roo.Element}
37752      */
37753     getEl : function(){
37754         return this.el;
37755     },
37756
37757     /**
37758      * Returns the specified region.
37759      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37760      * @return {Roo.LayoutRegion}
37761      */
37762     getRegion : function(target){
37763         return this.regions[target.toLowerCase()];
37764     },
37765
37766     onWindowResize : function(){
37767         if(this.monitorWindowResize){
37768             this.layout();
37769         }
37770     }
37771 });
37772 /*
37773  * Based on:
37774  * Ext JS Library 1.1.1
37775  * Copyright(c) 2006-2007, Ext JS, LLC.
37776  *
37777  * Originally Released Under LGPL - original licence link has changed is not relivant.
37778  *
37779  * Fork - LGPL
37780  * <script type="text/javascript">
37781  */
37782 /**
37783  * @class Roo.bootstrap.layout.Border
37784  * @extends Roo.bootstrap.layout.Manager
37785  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37786  * please see: examples/bootstrap/nested.html<br><br>
37787  
37788 <b>The container the layout is rendered into can be either the body element or any other element.
37789 If it is not the body element, the container needs to either be an absolute positioned element,
37790 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37791 the container size if it is not the body element.</b>
37792
37793 * @constructor
37794 * Create a new Border
37795 * @param {Object} config Configuration options
37796  */
37797 Roo.bootstrap.layout.Border = function(config){
37798     config = config || {};
37799     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37800     
37801     
37802     
37803     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37804         if(config[region]){
37805             config[region].region = region;
37806             this.addRegion(config[region]);
37807         }
37808     },this);
37809     
37810 };
37811
37812 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37813
37814 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37815     
37816     parent : false, // this might point to a 'nest' or a ???
37817     
37818     /**
37819      * Creates and adds a new region if it doesn't already exist.
37820      * @param {String} target The target region key (north, south, east, west or center).
37821      * @param {Object} config The regions config object
37822      * @return {BorderLayoutRegion} The new region
37823      */
37824     addRegion : function(config)
37825     {
37826         if(!this.regions[config.region]){
37827             var r = this.factory(config);
37828             this.bindRegion(r);
37829         }
37830         return this.regions[config.region];
37831     },
37832
37833     // private (kinda)
37834     bindRegion : function(r){
37835         this.regions[r.config.region] = r;
37836         
37837         r.on("visibilitychange",    this.layout, this);
37838         r.on("paneladded",          this.layout, this);
37839         r.on("panelremoved",        this.layout, this);
37840         r.on("invalidated",         this.layout, this);
37841         r.on("resized",             this.onRegionResized, this);
37842         r.on("collapsed",           this.onRegionCollapsed, this);
37843         r.on("expanded",            this.onRegionExpanded, this);
37844     },
37845
37846     /**
37847      * Performs a layout update.
37848      */
37849     layout : function()
37850     {
37851         if(this.updating) {
37852             return;
37853         }
37854         
37855         // render all the rebions if they have not been done alreayd?
37856         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37857             if(this.regions[region] && !this.regions[region].bodyEl){
37858                 this.regions[region].onRender(this.el)
37859             }
37860         },this);
37861         
37862         var size = this.getViewSize();
37863         var w = size.width;
37864         var h = size.height;
37865         var centerW = w;
37866         var centerH = h;
37867         var centerY = 0;
37868         var centerX = 0;
37869         //var x = 0, y = 0;
37870
37871         var rs = this.regions;
37872         var north = rs["north"];
37873         var south = rs["south"]; 
37874         var west = rs["west"];
37875         var east = rs["east"];
37876         var center = rs["center"];
37877         //if(this.hideOnLayout){ // not supported anymore
37878             //c.el.setStyle("display", "none");
37879         //}
37880         if(north && north.isVisible()){
37881             var b = north.getBox();
37882             var m = north.getMargins();
37883             b.width = w - (m.left+m.right);
37884             b.x = m.left;
37885             b.y = m.top;
37886             centerY = b.height + b.y + m.bottom;
37887             centerH -= centerY;
37888             north.updateBox(this.safeBox(b));
37889         }
37890         if(south && south.isVisible()){
37891             var b = south.getBox();
37892             var m = south.getMargins();
37893             b.width = w - (m.left+m.right);
37894             b.x = m.left;
37895             var totalHeight = (b.height + m.top + m.bottom);
37896             b.y = h - totalHeight + m.top;
37897             centerH -= totalHeight;
37898             south.updateBox(this.safeBox(b));
37899         }
37900         if(west && west.isVisible()){
37901             var b = west.getBox();
37902             var m = west.getMargins();
37903             b.height = centerH - (m.top+m.bottom);
37904             b.x = m.left;
37905             b.y = centerY + m.top;
37906             var totalWidth = (b.width + m.left + m.right);
37907             centerX += totalWidth;
37908             centerW -= totalWidth;
37909             west.updateBox(this.safeBox(b));
37910         }
37911         if(east && east.isVisible()){
37912             var b = east.getBox();
37913             var m = east.getMargins();
37914             b.height = centerH - (m.top+m.bottom);
37915             var totalWidth = (b.width + m.left + m.right);
37916             b.x = w - totalWidth + m.left;
37917             b.y = centerY + m.top;
37918             centerW -= totalWidth;
37919             east.updateBox(this.safeBox(b));
37920         }
37921         if(center){
37922             var m = center.getMargins();
37923             var centerBox = {
37924                 x: centerX + m.left,
37925                 y: centerY + m.top,
37926                 width: centerW - (m.left+m.right),
37927                 height: centerH - (m.top+m.bottom)
37928             };
37929             //if(this.hideOnLayout){
37930                 //center.el.setStyle("display", "block");
37931             //}
37932             center.updateBox(this.safeBox(centerBox));
37933         }
37934         this.el.repaint();
37935         this.fireEvent("layout", this);
37936     },
37937
37938     // private
37939     safeBox : function(box){
37940         box.width = Math.max(0, box.width);
37941         box.height = Math.max(0, box.height);
37942         return box;
37943     },
37944
37945     /**
37946      * Adds a ContentPanel (or subclass) to this layout.
37947      * @param {String} target The target region key (north, south, east, west or center).
37948      * @param {Roo.ContentPanel} panel The panel to add
37949      * @return {Roo.ContentPanel} The added panel
37950      */
37951     add : function(target, panel){
37952          
37953         target = target.toLowerCase();
37954         return this.regions[target].add(panel);
37955     },
37956
37957     /**
37958      * Remove a ContentPanel (or subclass) to this layout.
37959      * @param {String} target The target region key (north, south, east, west or center).
37960      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37961      * @return {Roo.ContentPanel} The removed panel
37962      */
37963     remove : function(target, panel){
37964         target = target.toLowerCase();
37965         return this.regions[target].remove(panel);
37966     },
37967
37968     /**
37969      * Searches all regions for a panel with the specified id
37970      * @param {String} panelId
37971      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37972      */
37973     findPanel : function(panelId){
37974         var rs = this.regions;
37975         for(var target in rs){
37976             if(typeof rs[target] != "function"){
37977                 var p = rs[target].getPanel(panelId);
37978                 if(p){
37979                     return p;
37980                 }
37981             }
37982         }
37983         return null;
37984     },
37985
37986     /**
37987      * Searches all regions for a panel with the specified id and activates (shows) it.
37988      * @param {String/ContentPanel} panelId The panels id or the panel itself
37989      * @return {Roo.ContentPanel} The shown panel or null
37990      */
37991     showPanel : function(panelId) {
37992       var rs = this.regions;
37993       for(var target in rs){
37994          var r = rs[target];
37995          if(typeof r != "function"){
37996             if(r.hasPanel(panelId)){
37997                return r.showPanel(panelId);
37998             }
37999          }
38000       }
38001       return null;
38002    },
38003
38004    /**
38005      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38006      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38007      */
38008    /*
38009     restoreState : function(provider){
38010         if(!provider){
38011             provider = Roo.state.Manager;
38012         }
38013         var sm = new Roo.LayoutStateManager();
38014         sm.init(this, provider);
38015     },
38016 */
38017  
38018  
38019     /**
38020      * Adds a xtype elements to the layout.
38021      * <pre><code>
38022
38023 layout.addxtype({
38024        xtype : 'ContentPanel',
38025        region: 'west',
38026        items: [ .... ]
38027    }
38028 );
38029
38030 layout.addxtype({
38031         xtype : 'NestedLayoutPanel',
38032         region: 'west',
38033         layout: {
38034            center: { },
38035            west: { }   
38036         },
38037         items : [ ... list of content panels or nested layout panels.. ]
38038    }
38039 );
38040 </code></pre>
38041      * @param {Object} cfg Xtype definition of item to add.
38042      */
38043     addxtype : function(cfg)
38044     {
38045         // basically accepts a pannel...
38046         // can accept a layout region..!?!?
38047         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38048         
38049         
38050         // theory?  children can only be panels??
38051         
38052         //if (!cfg.xtype.match(/Panel$/)) {
38053         //    return false;
38054         //}
38055         var ret = false;
38056         
38057         if (typeof(cfg.region) == 'undefined') {
38058             Roo.log("Failed to add Panel, region was not set");
38059             Roo.log(cfg);
38060             return false;
38061         }
38062         var region = cfg.region;
38063         delete cfg.region;
38064         
38065           
38066         var xitems = [];
38067         if (cfg.items) {
38068             xitems = cfg.items;
38069             delete cfg.items;
38070         }
38071         var nb = false;
38072         
38073         if ( region == 'center') {
38074             Roo.log("Center: " + cfg.title);
38075         }
38076         
38077         
38078         switch(cfg.xtype) 
38079         {
38080             case 'Content':  // ContentPanel (el, cfg)
38081             case 'Scroll':  // ContentPanel (el, cfg)
38082             case 'View': 
38083                 cfg.autoCreate = cfg.autoCreate || true;
38084                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38085                 //} else {
38086                 //    var el = this.el.createChild();
38087                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38088                 //}
38089                 
38090                 this.add(region, ret);
38091                 break;
38092             
38093             /*
38094             case 'TreePanel': // our new panel!
38095                 cfg.el = this.el.createChild();
38096                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38097                 this.add(region, ret);
38098                 break;
38099             */
38100             
38101             case 'Nest': 
38102                 // create a new Layout (which is  a Border Layout...
38103                 
38104                 var clayout = cfg.layout;
38105                 clayout.el  = this.el.createChild();
38106                 clayout.items   = clayout.items  || [];
38107                 
38108                 delete cfg.layout;
38109                 
38110                 // replace this exitems with the clayout ones..
38111                 xitems = clayout.items;
38112                  
38113                 // force background off if it's in center...
38114                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38115                     cfg.background = false;
38116                 }
38117                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38118                 
38119                 
38120                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38121                 //console.log('adding nested layout panel '  + cfg.toSource());
38122                 this.add(region, ret);
38123                 nb = {}; /// find first...
38124                 break;
38125             
38126             case 'Grid':
38127                 
38128                 // needs grid and region
38129                 
38130                 //var el = this.getRegion(region).el.createChild();
38131                 /*
38132                  *var el = this.el.createChild();
38133                 // create the grid first...
38134                 cfg.grid.container = el;
38135                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38136                 */
38137                 
38138                 if (region == 'center' && this.active ) {
38139                     cfg.background = false;
38140                 }
38141                 
38142                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38143                 
38144                 this.add(region, ret);
38145                 /*
38146                 if (cfg.background) {
38147                     // render grid on panel activation (if panel background)
38148                     ret.on('activate', function(gp) {
38149                         if (!gp.grid.rendered) {
38150                     //        gp.grid.render(el);
38151                         }
38152                     });
38153                 } else {
38154                   //  cfg.grid.render(el);
38155                 }
38156                 */
38157                 break;
38158            
38159            
38160             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38161                 // it was the old xcomponent building that caused this before.
38162                 // espeically if border is the top element in the tree.
38163                 ret = this;
38164                 break; 
38165                 
38166                     
38167                 
38168                 
38169                 
38170             default:
38171                 /*
38172                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38173                     
38174                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38175                     this.add(region, ret);
38176                 } else {
38177                 */
38178                     Roo.log(cfg);
38179                     throw "Can not add '" + cfg.xtype + "' to Border";
38180                     return null;
38181              
38182                                 
38183              
38184         }
38185         this.beginUpdate();
38186         // add children..
38187         var region = '';
38188         var abn = {};
38189         Roo.each(xitems, function(i)  {
38190             region = nb && i.region ? i.region : false;
38191             
38192             var add = ret.addxtype(i);
38193            
38194             if (region) {
38195                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38196                 if (!i.background) {
38197                     abn[region] = nb[region] ;
38198                 }
38199             }
38200             
38201         });
38202         this.endUpdate();
38203
38204         // make the last non-background panel active..
38205         //if (nb) { Roo.log(abn); }
38206         if (nb) {
38207             
38208             for(var r in abn) {
38209                 region = this.getRegion(r);
38210                 if (region) {
38211                     // tried using nb[r], but it does not work..
38212                      
38213                     region.showPanel(abn[r]);
38214                    
38215                 }
38216             }
38217         }
38218         return ret;
38219         
38220     },
38221     
38222     
38223 // private
38224     factory : function(cfg)
38225     {
38226         
38227         var validRegions = Roo.bootstrap.layout.Border.regions;
38228
38229         var target = cfg.region;
38230         cfg.mgr = this;
38231         
38232         var r = Roo.bootstrap.layout;
38233         Roo.log(target);
38234         switch(target){
38235             case "north":
38236                 return new r.North(cfg);
38237             case "south":
38238                 return new r.South(cfg);
38239             case "east":
38240                 return new r.East(cfg);
38241             case "west":
38242                 return new r.West(cfg);
38243             case "center":
38244                 return new r.Center(cfg);
38245         }
38246         throw 'Layout region "'+target+'" not supported.';
38247     }
38248     
38249     
38250 });
38251  /*
38252  * Based on:
38253  * Ext JS Library 1.1.1
38254  * Copyright(c) 2006-2007, Ext JS, LLC.
38255  *
38256  * Originally Released Under LGPL - original licence link has changed is not relivant.
38257  *
38258  * Fork - LGPL
38259  * <script type="text/javascript">
38260  */
38261  
38262 /**
38263  * @class Roo.bootstrap.layout.Basic
38264  * @extends Roo.util.Observable
38265  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38266  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38267  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38268  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38269  * @cfg {string}   region  the region that it inhabits..
38270  * @cfg {bool}   skipConfig skip config?
38271  * 
38272
38273  */
38274 Roo.bootstrap.layout.Basic = function(config){
38275     
38276     this.mgr = config.mgr;
38277     
38278     this.position = config.region;
38279     
38280     var skipConfig = config.skipConfig;
38281     
38282     this.events = {
38283         /**
38284          * @scope Roo.BasicLayoutRegion
38285          */
38286         
38287         /**
38288          * @event beforeremove
38289          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38290          * @param {Roo.LayoutRegion} this
38291          * @param {Roo.ContentPanel} panel The panel
38292          * @param {Object} e The cancel event object
38293          */
38294         "beforeremove" : true,
38295         /**
38296          * @event invalidated
38297          * Fires when the layout for this region is changed.
38298          * @param {Roo.LayoutRegion} this
38299          */
38300         "invalidated" : true,
38301         /**
38302          * @event visibilitychange
38303          * Fires when this region is shown or hidden 
38304          * @param {Roo.LayoutRegion} this
38305          * @param {Boolean} visibility true or false
38306          */
38307         "visibilitychange" : true,
38308         /**
38309          * @event paneladded
38310          * Fires when a panel is added. 
38311          * @param {Roo.LayoutRegion} this
38312          * @param {Roo.ContentPanel} panel The panel
38313          */
38314         "paneladded" : true,
38315         /**
38316          * @event panelremoved
38317          * Fires when a panel is removed. 
38318          * @param {Roo.LayoutRegion} this
38319          * @param {Roo.ContentPanel} panel The panel
38320          */
38321         "panelremoved" : true,
38322         /**
38323          * @event beforecollapse
38324          * Fires when this region before collapse.
38325          * @param {Roo.LayoutRegion} this
38326          */
38327         "beforecollapse" : true,
38328         /**
38329          * @event collapsed
38330          * Fires when this region is collapsed.
38331          * @param {Roo.LayoutRegion} this
38332          */
38333         "collapsed" : true,
38334         /**
38335          * @event expanded
38336          * Fires when this region is expanded.
38337          * @param {Roo.LayoutRegion} this
38338          */
38339         "expanded" : true,
38340         /**
38341          * @event slideshow
38342          * Fires when this region is slid into view.
38343          * @param {Roo.LayoutRegion} this
38344          */
38345         "slideshow" : true,
38346         /**
38347          * @event slidehide
38348          * Fires when this region slides out of view. 
38349          * @param {Roo.LayoutRegion} this
38350          */
38351         "slidehide" : true,
38352         /**
38353          * @event panelactivated
38354          * Fires when a panel is activated. 
38355          * @param {Roo.LayoutRegion} this
38356          * @param {Roo.ContentPanel} panel The activated panel
38357          */
38358         "panelactivated" : true,
38359         /**
38360          * @event resized
38361          * Fires when the user resizes this region. 
38362          * @param {Roo.LayoutRegion} this
38363          * @param {Number} newSize The new size (width for east/west, height for north/south)
38364          */
38365         "resized" : true
38366     };
38367     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38368     this.panels = new Roo.util.MixedCollection();
38369     this.panels.getKey = this.getPanelId.createDelegate(this);
38370     this.box = null;
38371     this.activePanel = null;
38372     // ensure listeners are added...
38373     
38374     if (config.listeners || config.events) {
38375         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38376             listeners : config.listeners || {},
38377             events : config.events || {}
38378         });
38379     }
38380     
38381     if(skipConfig !== true){
38382         this.applyConfig(config);
38383     }
38384 };
38385
38386 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38387 {
38388     getPanelId : function(p){
38389         return p.getId();
38390     },
38391     
38392     applyConfig : function(config){
38393         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38394         this.config = config;
38395         
38396     },
38397     
38398     /**
38399      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38400      * the width, for horizontal (north, south) the height.
38401      * @param {Number} newSize The new width or height
38402      */
38403     resizeTo : function(newSize){
38404         var el = this.el ? this.el :
38405                  (this.activePanel ? this.activePanel.getEl() : null);
38406         if(el){
38407             switch(this.position){
38408                 case "east":
38409                 case "west":
38410                     el.setWidth(newSize);
38411                     this.fireEvent("resized", this, newSize);
38412                 break;
38413                 case "north":
38414                 case "south":
38415                     el.setHeight(newSize);
38416                     this.fireEvent("resized", this, newSize);
38417                 break;                
38418             }
38419         }
38420     },
38421     
38422     getBox : function(){
38423         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38424     },
38425     
38426     getMargins : function(){
38427         return this.margins;
38428     },
38429     
38430     updateBox : function(box){
38431         this.box = box;
38432         var el = this.activePanel.getEl();
38433         el.dom.style.left = box.x + "px";
38434         el.dom.style.top = box.y + "px";
38435         this.activePanel.setSize(box.width, box.height);
38436     },
38437     
38438     /**
38439      * Returns the container element for this region.
38440      * @return {Roo.Element}
38441      */
38442     getEl : function(){
38443         return this.activePanel;
38444     },
38445     
38446     /**
38447      * Returns true if this region is currently visible.
38448      * @return {Boolean}
38449      */
38450     isVisible : function(){
38451         return this.activePanel ? true : false;
38452     },
38453     
38454     setActivePanel : function(panel){
38455         panel = this.getPanel(panel);
38456         if(this.activePanel && this.activePanel != panel){
38457             this.activePanel.setActiveState(false);
38458             this.activePanel.getEl().setLeftTop(-10000,-10000);
38459         }
38460         this.activePanel = panel;
38461         panel.setActiveState(true);
38462         if(this.box){
38463             panel.setSize(this.box.width, this.box.height);
38464         }
38465         this.fireEvent("panelactivated", this, panel);
38466         this.fireEvent("invalidated");
38467     },
38468     
38469     /**
38470      * Show the specified panel.
38471      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38472      * @return {Roo.ContentPanel} The shown panel or null
38473      */
38474     showPanel : function(panel){
38475         panel = this.getPanel(panel);
38476         if(panel){
38477             this.setActivePanel(panel);
38478         }
38479         return panel;
38480     },
38481     
38482     /**
38483      * Get the active panel for this region.
38484      * @return {Roo.ContentPanel} The active panel or null
38485      */
38486     getActivePanel : function(){
38487         return this.activePanel;
38488     },
38489     
38490     /**
38491      * Add the passed ContentPanel(s)
38492      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38493      * @return {Roo.ContentPanel} The panel added (if only one was added)
38494      */
38495     add : function(panel){
38496         if(arguments.length > 1){
38497             for(var i = 0, len = arguments.length; i < len; i++) {
38498                 this.add(arguments[i]);
38499             }
38500             return null;
38501         }
38502         if(this.hasPanel(panel)){
38503             this.showPanel(panel);
38504             return panel;
38505         }
38506         var el = panel.getEl();
38507         if(el.dom.parentNode != this.mgr.el.dom){
38508             this.mgr.el.dom.appendChild(el.dom);
38509         }
38510         if(panel.setRegion){
38511             panel.setRegion(this);
38512         }
38513         this.panels.add(panel);
38514         el.setStyle("position", "absolute");
38515         if(!panel.background){
38516             this.setActivePanel(panel);
38517             if(this.config.initialSize && this.panels.getCount()==1){
38518                 this.resizeTo(this.config.initialSize);
38519             }
38520         }
38521         this.fireEvent("paneladded", this, panel);
38522         return panel;
38523     },
38524     
38525     /**
38526      * Returns true if the panel is in this region.
38527      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38528      * @return {Boolean}
38529      */
38530     hasPanel : function(panel){
38531         if(typeof panel == "object"){ // must be panel obj
38532             panel = panel.getId();
38533         }
38534         return this.getPanel(panel) ? true : false;
38535     },
38536     
38537     /**
38538      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38539      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38540      * @param {Boolean} preservePanel Overrides the config preservePanel option
38541      * @return {Roo.ContentPanel} The panel that was removed
38542      */
38543     remove : function(panel, preservePanel){
38544         panel = this.getPanel(panel);
38545         if(!panel){
38546             return null;
38547         }
38548         var e = {};
38549         this.fireEvent("beforeremove", this, panel, e);
38550         if(e.cancel === true){
38551             return null;
38552         }
38553         var panelId = panel.getId();
38554         this.panels.removeKey(panelId);
38555         return panel;
38556     },
38557     
38558     /**
38559      * Returns the panel specified or null if it's not in this region.
38560      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38561      * @return {Roo.ContentPanel}
38562      */
38563     getPanel : function(id){
38564         if(typeof id == "object"){ // must be panel obj
38565             return id;
38566         }
38567         return this.panels.get(id);
38568     },
38569     
38570     /**
38571      * Returns this regions position (north/south/east/west/center).
38572      * @return {String} 
38573      */
38574     getPosition: function(){
38575         return this.position;    
38576     }
38577 });/*
38578  * Based on:
38579  * Ext JS Library 1.1.1
38580  * Copyright(c) 2006-2007, Ext JS, LLC.
38581  *
38582  * Originally Released Under LGPL - original licence link has changed is not relivant.
38583  *
38584  * Fork - LGPL
38585  * <script type="text/javascript">
38586  */
38587  
38588 /**
38589  * @class Roo.bootstrap.layout.Region
38590  * @extends Roo.bootstrap.layout.Basic
38591  * This class represents a region in a layout manager.
38592  
38593  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38594  * @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})
38595  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38596  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38597  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38598  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38599  * @cfg {String}    title           The title for the region (overrides panel titles)
38600  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38601  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38602  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38603  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38604  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38605  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38606  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38607  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38608  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38609  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38610
38611  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38612  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38613  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38614  * @cfg {Number}    width           For East/West panels
38615  * @cfg {Number}    height          For North/South panels
38616  * @cfg {Boolean}   split           To show the splitter
38617  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38618  * 
38619  * @cfg {string}   cls             Extra CSS classes to add to region
38620  * 
38621  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38622  * @cfg {string}   region  the region that it inhabits..
38623  *
38624
38625  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38626  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38627
38628  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38629  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38630  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38631  */
38632 Roo.bootstrap.layout.Region = function(config)
38633 {
38634     this.applyConfig(config);
38635
38636     var mgr = config.mgr;
38637     var pos = config.region;
38638     config.skipConfig = true;
38639     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38640     
38641     if (mgr.el) {
38642         this.onRender(mgr.el);   
38643     }
38644      
38645     this.visible = true;
38646     this.collapsed = false;
38647     this.unrendered_panels = [];
38648 };
38649
38650 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38651
38652     position: '', // set by wrapper (eg. north/south etc..)
38653     unrendered_panels : null,  // unrendered panels.
38654     
38655     tabPosition : false,
38656     
38657     mgr: false, // points to 'Border'
38658     
38659     
38660     createBody : function(){
38661         /** This region's body element 
38662         * @type Roo.Element */
38663         this.bodyEl = this.el.createChild({
38664                 tag: "div",
38665                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38666         });
38667     },
38668
38669     onRender: function(ctr, pos)
38670     {
38671         var dh = Roo.DomHelper;
38672         /** This region's container element 
38673         * @type Roo.Element */
38674         this.el = dh.append(ctr.dom, {
38675                 tag: "div",
38676                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38677             }, true);
38678         /** This region's title element 
38679         * @type Roo.Element */
38680     
38681         this.titleEl = dh.append(this.el.dom,  {
38682                 tag: "div",
38683                 unselectable: "on",
38684                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38685                 children:[
38686                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38687                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38688                 ]
38689             }, true);
38690         
38691         this.titleEl.enableDisplayMode();
38692         /** This region's title text element 
38693         * @type HTMLElement */
38694         this.titleTextEl = this.titleEl.dom.firstChild;
38695         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38696         /*
38697         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38698         this.closeBtn.enableDisplayMode();
38699         this.closeBtn.on("click", this.closeClicked, this);
38700         this.closeBtn.hide();
38701     */
38702         this.createBody(this.config);
38703         if(this.config.hideWhenEmpty){
38704             this.hide();
38705             this.on("paneladded", this.validateVisibility, this);
38706             this.on("panelremoved", this.validateVisibility, this);
38707         }
38708         if(this.autoScroll){
38709             this.bodyEl.setStyle("overflow", "auto");
38710         }else{
38711             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38712         }
38713         //if(c.titlebar !== false){
38714             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38715                 this.titleEl.hide();
38716             }else{
38717                 this.titleEl.show();
38718                 if(this.config.title){
38719                     this.titleTextEl.innerHTML = this.config.title;
38720                 }
38721             }
38722         //}
38723         if(this.config.collapsed){
38724             this.collapse(true);
38725         }
38726         if(this.config.hidden){
38727             this.hide();
38728         }
38729         
38730         if (this.unrendered_panels && this.unrendered_panels.length) {
38731             for (var i =0;i< this.unrendered_panels.length; i++) {
38732                 this.add(this.unrendered_panels[i]);
38733             }
38734             this.unrendered_panels = null;
38735             
38736         }
38737         
38738     },
38739     
38740     applyConfig : function(c)
38741     {
38742         /*
38743          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38744             var dh = Roo.DomHelper;
38745             if(c.titlebar !== false){
38746                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38747                 this.collapseBtn.on("click", this.collapse, this);
38748                 this.collapseBtn.enableDisplayMode();
38749                 /*
38750                 if(c.showPin === true || this.showPin){
38751                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38752                     this.stickBtn.enableDisplayMode();
38753                     this.stickBtn.on("click", this.expand, this);
38754                     this.stickBtn.hide();
38755                 }
38756                 
38757             }
38758             */
38759             /** This region's collapsed element
38760             * @type Roo.Element */
38761             /*
38762              *
38763             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38764                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38765             ]}, true);
38766             
38767             if(c.floatable !== false){
38768                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38769                this.collapsedEl.on("click", this.collapseClick, this);
38770             }
38771
38772             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38773                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38774                    id: "message", unselectable: "on", style:{"float":"left"}});
38775                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38776              }
38777             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38778             this.expandBtn.on("click", this.expand, this);
38779             
38780         }
38781         
38782         if(this.collapseBtn){
38783             this.collapseBtn.setVisible(c.collapsible == true);
38784         }
38785         
38786         this.cmargins = c.cmargins || this.cmargins ||
38787                          (this.position == "west" || this.position == "east" ?
38788                              {top: 0, left: 2, right:2, bottom: 0} :
38789                              {top: 2, left: 0, right:0, bottom: 2});
38790         */
38791         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38792         
38793         
38794         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38795         
38796         this.autoScroll = c.autoScroll || false;
38797         
38798         
38799        
38800         
38801         this.duration = c.duration || .30;
38802         this.slideDuration = c.slideDuration || .45;
38803         this.config = c;
38804        
38805     },
38806     /**
38807      * Returns true if this region is currently visible.
38808      * @return {Boolean}
38809      */
38810     isVisible : function(){
38811         return this.visible;
38812     },
38813
38814     /**
38815      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38816      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38817      */
38818     //setCollapsedTitle : function(title){
38819     //    title = title || "&#160;";
38820      //   if(this.collapsedTitleTextEl){
38821       //      this.collapsedTitleTextEl.innerHTML = title;
38822        // }
38823     //},
38824
38825     getBox : function(){
38826         var b;
38827       //  if(!this.collapsed){
38828             b = this.el.getBox(false, true);
38829        // }else{
38830           //  b = this.collapsedEl.getBox(false, true);
38831         //}
38832         return b;
38833     },
38834
38835     getMargins : function(){
38836         return this.margins;
38837         //return this.collapsed ? this.cmargins : this.margins;
38838     },
38839 /*
38840     highlight : function(){
38841         this.el.addClass("x-layout-panel-dragover");
38842     },
38843
38844     unhighlight : function(){
38845         this.el.removeClass("x-layout-panel-dragover");
38846     },
38847 */
38848     updateBox : function(box)
38849     {
38850         if (!this.bodyEl) {
38851             return; // not rendered yet..
38852         }
38853         
38854         this.box = box;
38855         if(!this.collapsed){
38856             this.el.dom.style.left = box.x + "px";
38857             this.el.dom.style.top = box.y + "px";
38858             this.updateBody(box.width, box.height);
38859         }else{
38860             this.collapsedEl.dom.style.left = box.x + "px";
38861             this.collapsedEl.dom.style.top = box.y + "px";
38862             this.collapsedEl.setSize(box.width, box.height);
38863         }
38864         if(this.tabs){
38865             this.tabs.autoSizeTabs();
38866         }
38867     },
38868
38869     updateBody : function(w, h)
38870     {
38871         if(w !== null){
38872             this.el.setWidth(w);
38873             w -= this.el.getBorderWidth("rl");
38874             if(this.config.adjustments){
38875                 w += this.config.adjustments[0];
38876             }
38877         }
38878         if(h !== null && h > 0){
38879             this.el.setHeight(h);
38880             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38881             h -= this.el.getBorderWidth("tb");
38882             if(this.config.adjustments){
38883                 h += this.config.adjustments[1];
38884             }
38885             this.bodyEl.setHeight(h);
38886             if(this.tabs){
38887                 h = this.tabs.syncHeight(h);
38888             }
38889         }
38890         if(this.panelSize){
38891             w = w !== null ? w : this.panelSize.width;
38892             h = h !== null ? h : this.panelSize.height;
38893         }
38894         if(this.activePanel){
38895             var el = this.activePanel.getEl();
38896             w = w !== null ? w : el.getWidth();
38897             h = h !== null ? h : el.getHeight();
38898             this.panelSize = {width: w, height: h};
38899             this.activePanel.setSize(w, h);
38900         }
38901         if(Roo.isIE && this.tabs){
38902             this.tabs.el.repaint();
38903         }
38904     },
38905
38906     /**
38907      * Returns the container element for this region.
38908      * @return {Roo.Element}
38909      */
38910     getEl : function(){
38911         return this.el;
38912     },
38913
38914     /**
38915      * Hides this region.
38916      */
38917     hide : function(){
38918         //if(!this.collapsed){
38919             this.el.dom.style.left = "-2000px";
38920             this.el.hide();
38921         //}else{
38922          //   this.collapsedEl.dom.style.left = "-2000px";
38923          //   this.collapsedEl.hide();
38924        // }
38925         this.visible = false;
38926         this.fireEvent("visibilitychange", this, false);
38927     },
38928
38929     /**
38930      * Shows this region if it was previously hidden.
38931      */
38932     show : function(){
38933         //if(!this.collapsed){
38934             this.el.show();
38935         //}else{
38936         //    this.collapsedEl.show();
38937        // }
38938         this.visible = true;
38939         this.fireEvent("visibilitychange", this, true);
38940     },
38941 /*
38942     closeClicked : function(){
38943         if(this.activePanel){
38944             this.remove(this.activePanel);
38945         }
38946     },
38947
38948     collapseClick : function(e){
38949         if(this.isSlid){
38950            e.stopPropagation();
38951            this.slideIn();
38952         }else{
38953            e.stopPropagation();
38954            this.slideOut();
38955         }
38956     },
38957 */
38958     /**
38959      * Collapses this region.
38960      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38961      */
38962     /*
38963     collapse : function(skipAnim, skipCheck = false){
38964         if(this.collapsed) {
38965             return;
38966         }
38967         
38968         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38969             
38970             this.collapsed = true;
38971             if(this.split){
38972                 this.split.el.hide();
38973             }
38974             if(this.config.animate && skipAnim !== true){
38975                 this.fireEvent("invalidated", this);
38976                 this.animateCollapse();
38977             }else{
38978                 this.el.setLocation(-20000,-20000);
38979                 this.el.hide();
38980                 this.collapsedEl.show();
38981                 this.fireEvent("collapsed", this);
38982                 this.fireEvent("invalidated", this);
38983             }
38984         }
38985         
38986     },
38987 */
38988     animateCollapse : function(){
38989         // overridden
38990     },
38991
38992     /**
38993      * Expands this region if it was previously collapsed.
38994      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38995      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38996      */
38997     /*
38998     expand : function(e, skipAnim){
38999         if(e) {
39000             e.stopPropagation();
39001         }
39002         if(!this.collapsed || this.el.hasActiveFx()) {
39003             return;
39004         }
39005         if(this.isSlid){
39006             this.afterSlideIn();
39007             skipAnim = true;
39008         }
39009         this.collapsed = false;
39010         if(this.config.animate && skipAnim !== true){
39011             this.animateExpand();
39012         }else{
39013             this.el.show();
39014             if(this.split){
39015                 this.split.el.show();
39016             }
39017             this.collapsedEl.setLocation(-2000,-2000);
39018             this.collapsedEl.hide();
39019             this.fireEvent("invalidated", this);
39020             this.fireEvent("expanded", this);
39021         }
39022     },
39023 */
39024     animateExpand : function(){
39025         // overridden
39026     },
39027
39028     initTabs : function()
39029     {
39030         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39031         
39032         var ts = new Roo.bootstrap.panel.Tabs({
39033             el: this.bodyEl.dom,
39034             region : this,
39035             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39036             disableTooltips: this.config.disableTabTips,
39037             toolbar : this.config.toolbar
39038         });
39039         
39040         if(this.config.hideTabs){
39041             ts.stripWrap.setDisplayed(false);
39042         }
39043         this.tabs = ts;
39044         ts.resizeTabs = this.config.resizeTabs === true;
39045         ts.minTabWidth = this.config.minTabWidth || 40;
39046         ts.maxTabWidth = this.config.maxTabWidth || 250;
39047         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39048         ts.monitorResize = false;
39049         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39050         ts.bodyEl.addClass('roo-layout-tabs-body');
39051         this.panels.each(this.initPanelAsTab, this);
39052     },
39053
39054     initPanelAsTab : function(panel){
39055         var ti = this.tabs.addTab(
39056             panel.getEl().id,
39057             panel.getTitle(),
39058             null,
39059             this.config.closeOnTab && panel.isClosable(),
39060             panel.tpl
39061         );
39062         if(panel.tabTip !== undefined){
39063             ti.setTooltip(panel.tabTip);
39064         }
39065         ti.on("activate", function(){
39066               this.setActivePanel(panel);
39067         }, this);
39068         
39069         if(this.config.closeOnTab){
39070             ti.on("beforeclose", function(t, e){
39071                 e.cancel = true;
39072                 this.remove(panel);
39073             }, this);
39074         }
39075         
39076         panel.tabItem = ti;
39077         
39078         return ti;
39079     },
39080
39081     updatePanelTitle : function(panel, title)
39082     {
39083         if(this.activePanel == panel){
39084             this.updateTitle(title);
39085         }
39086         if(this.tabs){
39087             var ti = this.tabs.getTab(panel.getEl().id);
39088             ti.setText(title);
39089             if(panel.tabTip !== undefined){
39090                 ti.setTooltip(panel.tabTip);
39091             }
39092         }
39093     },
39094
39095     updateTitle : function(title){
39096         if(this.titleTextEl && !this.config.title){
39097             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39098         }
39099     },
39100
39101     setActivePanel : function(panel)
39102     {
39103         panel = this.getPanel(panel);
39104         if(this.activePanel && this.activePanel != panel){
39105             if(this.activePanel.setActiveState(false) === false){
39106                 return;
39107             }
39108         }
39109         this.activePanel = panel;
39110         panel.setActiveState(true);
39111         if(this.panelSize){
39112             panel.setSize(this.panelSize.width, this.panelSize.height);
39113         }
39114         if(this.closeBtn){
39115             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39116         }
39117         this.updateTitle(panel.getTitle());
39118         if(this.tabs){
39119             this.fireEvent("invalidated", this);
39120         }
39121         this.fireEvent("panelactivated", this, panel);
39122     },
39123
39124     /**
39125      * Shows the specified panel.
39126      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39127      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39128      */
39129     showPanel : function(panel)
39130     {
39131         panel = this.getPanel(panel);
39132         if(panel){
39133             if(this.tabs){
39134                 var tab = this.tabs.getTab(panel.getEl().id);
39135                 if(tab.isHidden()){
39136                     this.tabs.unhideTab(tab.id);
39137                 }
39138                 tab.activate();
39139             }else{
39140                 this.setActivePanel(panel);
39141             }
39142         }
39143         return panel;
39144     },
39145
39146     /**
39147      * Get the active panel for this region.
39148      * @return {Roo.ContentPanel} The active panel or null
39149      */
39150     getActivePanel : function(){
39151         return this.activePanel;
39152     },
39153
39154     validateVisibility : function(){
39155         if(this.panels.getCount() < 1){
39156             this.updateTitle("&#160;");
39157             this.closeBtn.hide();
39158             this.hide();
39159         }else{
39160             if(!this.isVisible()){
39161                 this.show();
39162             }
39163         }
39164     },
39165
39166     /**
39167      * Adds the passed ContentPanel(s) to this region.
39168      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39169      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39170      */
39171     add : function(panel)
39172     {
39173         if(arguments.length > 1){
39174             for(var i = 0, len = arguments.length; i < len; i++) {
39175                 this.add(arguments[i]);
39176             }
39177             return null;
39178         }
39179         
39180         // if we have not been rendered yet, then we can not really do much of this..
39181         if (!this.bodyEl) {
39182             this.unrendered_panels.push(panel);
39183             return panel;
39184         }
39185         
39186         
39187         
39188         
39189         if(this.hasPanel(panel)){
39190             this.showPanel(panel);
39191             return panel;
39192         }
39193         panel.setRegion(this);
39194         this.panels.add(panel);
39195        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39196             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39197             // and hide them... ???
39198             this.bodyEl.dom.appendChild(panel.getEl().dom);
39199             if(panel.background !== true){
39200                 this.setActivePanel(panel);
39201             }
39202             this.fireEvent("paneladded", this, panel);
39203             return panel;
39204         }
39205         */
39206         if(!this.tabs){
39207             this.initTabs();
39208         }else{
39209             this.initPanelAsTab(panel);
39210         }
39211         
39212         
39213         if(panel.background !== true){
39214             this.tabs.activate(panel.getEl().id);
39215         }
39216         this.fireEvent("paneladded", this, panel);
39217         return panel;
39218     },
39219
39220     /**
39221      * Hides the tab for the specified panel.
39222      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39223      */
39224     hidePanel : function(panel){
39225         if(this.tabs && (panel = this.getPanel(panel))){
39226             this.tabs.hideTab(panel.getEl().id);
39227         }
39228     },
39229
39230     /**
39231      * Unhides the tab for a previously hidden panel.
39232      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39233      */
39234     unhidePanel : function(panel){
39235         if(this.tabs && (panel = this.getPanel(panel))){
39236             this.tabs.unhideTab(panel.getEl().id);
39237         }
39238     },
39239
39240     clearPanels : function(){
39241         while(this.panels.getCount() > 0){
39242              this.remove(this.panels.first());
39243         }
39244     },
39245
39246     /**
39247      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39249      * @param {Boolean} preservePanel Overrides the config preservePanel option
39250      * @return {Roo.ContentPanel} The panel that was removed
39251      */
39252     remove : function(panel, preservePanel)
39253     {
39254         panel = this.getPanel(panel);
39255         if(!panel){
39256             return null;
39257         }
39258         var e = {};
39259         this.fireEvent("beforeremove", this, panel, e);
39260         if(e.cancel === true){
39261             return null;
39262         }
39263         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39264         var panelId = panel.getId();
39265         this.panels.removeKey(panelId);
39266         if(preservePanel){
39267             document.body.appendChild(panel.getEl().dom);
39268         }
39269         if(this.tabs){
39270             this.tabs.removeTab(panel.getEl().id);
39271         }else if (!preservePanel){
39272             this.bodyEl.dom.removeChild(panel.getEl().dom);
39273         }
39274         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39275             var p = this.panels.first();
39276             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39277             tempEl.appendChild(p.getEl().dom);
39278             this.bodyEl.update("");
39279             this.bodyEl.dom.appendChild(p.getEl().dom);
39280             tempEl = null;
39281             this.updateTitle(p.getTitle());
39282             this.tabs = null;
39283             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39284             this.setActivePanel(p);
39285         }
39286         panel.setRegion(null);
39287         if(this.activePanel == panel){
39288             this.activePanel = null;
39289         }
39290         if(this.config.autoDestroy !== false && preservePanel !== true){
39291             try{panel.destroy();}catch(e){}
39292         }
39293         this.fireEvent("panelremoved", this, panel);
39294         return panel;
39295     },
39296
39297     /**
39298      * Returns the TabPanel component used by this region
39299      * @return {Roo.TabPanel}
39300      */
39301     getTabs : function(){
39302         return this.tabs;
39303     },
39304
39305     createTool : function(parentEl, className){
39306         var btn = Roo.DomHelper.append(parentEl, {
39307             tag: "div",
39308             cls: "x-layout-tools-button",
39309             children: [ {
39310                 tag: "div",
39311                 cls: "roo-layout-tools-button-inner " + className,
39312                 html: "&#160;"
39313             }]
39314         }, true);
39315         btn.addClassOnOver("roo-layout-tools-button-over");
39316         return btn;
39317     }
39318 });/*
39319  * Based on:
39320  * Ext JS Library 1.1.1
39321  * Copyright(c) 2006-2007, Ext JS, LLC.
39322  *
39323  * Originally Released Under LGPL - original licence link has changed is not relivant.
39324  *
39325  * Fork - LGPL
39326  * <script type="text/javascript">
39327  */
39328  
39329
39330
39331 /**
39332  * @class Roo.SplitLayoutRegion
39333  * @extends Roo.LayoutRegion
39334  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39335  */
39336 Roo.bootstrap.layout.Split = function(config){
39337     this.cursor = config.cursor;
39338     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39339 };
39340
39341 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39342 {
39343     splitTip : "Drag to resize.",
39344     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39345     useSplitTips : false,
39346
39347     applyConfig : function(config){
39348         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39349     },
39350     
39351     onRender : function(ctr,pos) {
39352         
39353         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39354         if(!this.config.split){
39355             return;
39356         }
39357         if(!this.split){
39358             
39359             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39360                             tag: "div",
39361                             id: this.el.id + "-split",
39362                             cls: "roo-layout-split roo-layout-split-"+this.position,
39363                             html: "&#160;"
39364             });
39365             /** The SplitBar for this region 
39366             * @type Roo.SplitBar */
39367             // does not exist yet...
39368             Roo.log([this.position, this.orientation]);
39369             
39370             this.split = new Roo.bootstrap.SplitBar({
39371                 dragElement : splitEl,
39372                 resizingElement: this.el,
39373                 orientation : this.orientation
39374             });
39375             
39376             this.split.on("moved", this.onSplitMove, this);
39377             this.split.useShim = this.config.useShim === true;
39378             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39379             if(this.useSplitTips){
39380                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39381             }
39382             //if(config.collapsible){
39383             //    this.split.el.on("dblclick", this.collapse,  this);
39384             //}
39385         }
39386         if(typeof this.config.minSize != "undefined"){
39387             this.split.minSize = this.config.minSize;
39388         }
39389         if(typeof this.config.maxSize != "undefined"){
39390             this.split.maxSize = this.config.maxSize;
39391         }
39392         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39393             this.hideSplitter();
39394         }
39395         
39396     },
39397
39398     getHMaxSize : function(){
39399          var cmax = this.config.maxSize || 10000;
39400          var center = this.mgr.getRegion("center");
39401          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39402     },
39403
39404     getVMaxSize : function(){
39405          var cmax = this.config.maxSize || 10000;
39406          var center = this.mgr.getRegion("center");
39407          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39408     },
39409
39410     onSplitMove : function(split, newSize){
39411         this.fireEvent("resized", this, newSize);
39412     },
39413     
39414     /** 
39415      * Returns the {@link Roo.SplitBar} for this region.
39416      * @return {Roo.SplitBar}
39417      */
39418     getSplitBar : function(){
39419         return this.split;
39420     },
39421     
39422     hide : function(){
39423         this.hideSplitter();
39424         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39425     },
39426
39427     hideSplitter : function(){
39428         if(this.split){
39429             this.split.el.setLocation(-2000,-2000);
39430             this.split.el.hide();
39431         }
39432     },
39433
39434     show : function(){
39435         if(this.split){
39436             this.split.el.show();
39437         }
39438         Roo.bootstrap.layout.Split.superclass.show.call(this);
39439     },
39440     
39441     beforeSlide: function(){
39442         if(Roo.isGecko){// firefox overflow auto bug workaround
39443             this.bodyEl.clip();
39444             if(this.tabs) {
39445                 this.tabs.bodyEl.clip();
39446             }
39447             if(this.activePanel){
39448                 this.activePanel.getEl().clip();
39449                 
39450                 if(this.activePanel.beforeSlide){
39451                     this.activePanel.beforeSlide();
39452                 }
39453             }
39454         }
39455     },
39456     
39457     afterSlide : function(){
39458         if(Roo.isGecko){// firefox overflow auto bug workaround
39459             this.bodyEl.unclip();
39460             if(this.tabs) {
39461                 this.tabs.bodyEl.unclip();
39462             }
39463             if(this.activePanel){
39464                 this.activePanel.getEl().unclip();
39465                 if(this.activePanel.afterSlide){
39466                     this.activePanel.afterSlide();
39467                 }
39468             }
39469         }
39470     },
39471
39472     initAutoHide : function(){
39473         if(this.autoHide !== false){
39474             if(!this.autoHideHd){
39475                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39476                 this.autoHideHd = {
39477                     "mouseout": function(e){
39478                         if(!e.within(this.el, true)){
39479                             st.delay(500);
39480                         }
39481                     },
39482                     "mouseover" : function(e){
39483                         st.cancel();
39484                     },
39485                     scope : this
39486                 };
39487             }
39488             this.el.on(this.autoHideHd);
39489         }
39490     },
39491
39492     clearAutoHide : function(){
39493         if(this.autoHide !== false){
39494             this.el.un("mouseout", this.autoHideHd.mouseout);
39495             this.el.un("mouseover", this.autoHideHd.mouseover);
39496         }
39497     },
39498
39499     clearMonitor : function(){
39500         Roo.get(document).un("click", this.slideInIf, this);
39501     },
39502
39503     // these names are backwards but not changed for compat
39504     slideOut : function(){
39505         if(this.isSlid || this.el.hasActiveFx()){
39506             return;
39507         }
39508         this.isSlid = true;
39509         if(this.collapseBtn){
39510             this.collapseBtn.hide();
39511         }
39512         this.closeBtnState = this.closeBtn.getStyle('display');
39513         this.closeBtn.hide();
39514         if(this.stickBtn){
39515             this.stickBtn.show();
39516         }
39517         this.el.show();
39518         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39519         this.beforeSlide();
39520         this.el.setStyle("z-index", 10001);
39521         this.el.slideIn(this.getSlideAnchor(), {
39522             callback: function(){
39523                 this.afterSlide();
39524                 this.initAutoHide();
39525                 Roo.get(document).on("click", this.slideInIf, this);
39526                 this.fireEvent("slideshow", this);
39527             },
39528             scope: this,
39529             block: true
39530         });
39531     },
39532
39533     afterSlideIn : function(){
39534         this.clearAutoHide();
39535         this.isSlid = false;
39536         this.clearMonitor();
39537         this.el.setStyle("z-index", "");
39538         if(this.collapseBtn){
39539             this.collapseBtn.show();
39540         }
39541         this.closeBtn.setStyle('display', this.closeBtnState);
39542         if(this.stickBtn){
39543             this.stickBtn.hide();
39544         }
39545         this.fireEvent("slidehide", this);
39546     },
39547
39548     slideIn : function(cb){
39549         if(!this.isSlid || this.el.hasActiveFx()){
39550             Roo.callback(cb);
39551             return;
39552         }
39553         this.isSlid = false;
39554         this.beforeSlide();
39555         this.el.slideOut(this.getSlideAnchor(), {
39556             callback: function(){
39557                 this.el.setLeftTop(-10000, -10000);
39558                 this.afterSlide();
39559                 this.afterSlideIn();
39560                 Roo.callback(cb);
39561             },
39562             scope: this,
39563             block: true
39564         });
39565     },
39566     
39567     slideInIf : function(e){
39568         if(!e.within(this.el)){
39569             this.slideIn();
39570         }
39571     },
39572
39573     animateCollapse : function(){
39574         this.beforeSlide();
39575         this.el.setStyle("z-index", 20000);
39576         var anchor = this.getSlideAnchor();
39577         this.el.slideOut(anchor, {
39578             callback : function(){
39579                 this.el.setStyle("z-index", "");
39580                 this.collapsedEl.slideIn(anchor, {duration:.3});
39581                 this.afterSlide();
39582                 this.el.setLocation(-10000,-10000);
39583                 this.el.hide();
39584                 this.fireEvent("collapsed", this);
39585             },
39586             scope: this,
39587             block: true
39588         });
39589     },
39590
39591     animateExpand : function(){
39592         this.beforeSlide();
39593         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39594         this.el.setStyle("z-index", 20000);
39595         this.collapsedEl.hide({
39596             duration:.1
39597         });
39598         this.el.slideIn(this.getSlideAnchor(), {
39599             callback : function(){
39600                 this.el.setStyle("z-index", "");
39601                 this.afterSlide();
39602                 if(this.split){
39603                     this.split.el.show();
39604                 }
39605                 this.fireEvent("invalidated", this);
39606                 this.fireEvent("expanded", this);
39607             },
39608             scope: this,
39609             block: true
39610         });
39611     },
39612
39613     anchors : {
39614         "west" : "left",
39615         "east" : "right",
39616         "north" : "top",
39617         "south" : "bottom"
39618     },
39619
39620     sanchors : {
39621         "west" : "l",
39622         "east" : "r",
39623         "north" : "t",
39624         "south" : "b"
39625     },
39626
39627     canchors : {
39628         "west" : "tl-tr",
39629         "east" : "tr-tl",
39630         "north" : "tl-bl",
39631         "south" : "bl-tl"
39632     },
39633
39634     getAnchor : function(){
39635         return this.anchors[this.position];
39636     },
39637
39638     getCollapseAnchor : function(){
39639         return this.canchors[this.position];
39640     },
39641
39642     getSlideAnchor : function(){
39643         return this.sanchors[this.position];
39644     },
39645
39646     getAlignAdj : function(){
39647         var cm = this.cmargins;
39648         switch(this.position){
39649             case "west":
39650                 return [0, 0];
39651             break;
39652             case "east":
39653                 return [0, 0];
39654             break;
39655             case "north":
39656                 return [0, 0];
39657             break;
39658             case "south":
39659                 return [0, 0];
39660             break;
39661         }
39662     },
39663
39664     getExpandAdj : function(){
39665         var c = this.collapsedEl, cm = this.cmargins;
39666         switch(this.position){
39667             case "west":
39668                 return [-(cm.right+c.getWidth()+cm.left), 0];
39669             break;
39670             case "east":
39671                 return [cm.right+c.getWidth()+cm.left, 0];
39672             break;
39673             case "north":
39674                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39675             break;
39676             case "south":
39677                 return [0, cm.top+cm.bottom+c.getHeight()];
39678             break;
39679         }
39680     }
39681 });/*
39682  * Based on:
39683  * Ext JS Library 1.1.1
39684  * Copyright(c) 2006-2007, Ext JS, LLC.
39685  *
39686  * Originally Released Under LGPL - original licence link has changed is not relivant.
39687  *
39688  * Fork - LGPL
39689  * <script type="text/javascript">
39690  */
39691 /*
39692  * These classes are private internal classes
39693  */
39694 Roo.bootstrap.layout.Center = function(config){
39695     config.region = "center";
39696     Roo.bootstrap.layout.Region.call(this, config);
39697     this.visible = true;
39698     this.minWidth = config.minWidth || 20;
39699     this.minHeight = config.minHeight || 20;
39700 };
39701
39702 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39703     hide : function(){
39704         // center panel can't be hidden
39705     },
39706     
39707     show : function(){
39708         // center panel can't be hidden
39709     },
39710     
39711     getMinWidth: function(){
39712         return this.minWidth;
39713     },
39714     
39715     getMinHeight: function(){
39716         return this.minHeight;
39717     }
39718 });
39719
39720
39721
39722
39723  
39724
39725
39726
39727
39728
39729
39730 Roo.bootstrap.layout.North = function(config)
39731 {
39732     config.region = 'north';
39733     config.cursor = 'n-resize';
39734     
39735     Roo.bootstrap.layout.Split.call(this, config);
39736     
39737     
39738     if(this.split){
39739         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39740         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39741         this.split.el.addClass("roo-layout-split-v");
39742     }
39743     //var size = config.initialSize || config.height;
39744     //if(this.el && typeof size != "undefined"){
39745     //    this.el.setHeight(size);
39746     //}
39747 };
39748 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39749 {
39750     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39751      
39752      
39753     onRender : function(ctr, pos)
39754     {
39755         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39756         var size = this.config.initialSize || this.config.height;
39757         if(this.el && typeof size != "undefined"){
39758             this.el.setHeight(size);
39759         }
39760     
39761     },
39762     
39763     getBox : function(){
39764         if(this.collapsed){
39765             return this.collapsedEl.getBox();
39766         }
39767         var box = this.el.getBox();
39768         if(this.split){
39769             box.height += this.split.el.getHeight();
39770         }
39771         return box;
39772     },
39773     
39774     updateBox : function(box){
39775         if(this.split && !this.collapsed){
39776             box.height -= this.split.el.getHeight();
39777             this.split.el.setLeft(box.x);
39778             this.split.el.setTop(box.y+box.height);
39779             this.split.el.setWidth(box.width);
39780         }
39781         if(this.collapsed){
39782             this.updateBody(box.width, null);
39783         }
39784         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39785     }
39786 });
39787
39788
39789
39790
39791
39792 Roo.bootstrap.layout.South = function(config){
39793     config.region = 'south';
39794     config.cursor = 's-resize';
39795     Roo.bootstrap.layout.Split.call(this, config);
39796     if(this.split){
39797         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39798         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39799         this.split.el.addClass("roo-layout-split-v");
39800     }
39801     
39802 };
39803
39804 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39805     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39806     
39807     onRender : function(ctr, pos)
39808     {
39809         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39810         var size = this.config.initialSize || this.config.height;
39811         if(this.el && typeof size != "undefined"){
39812             this.el.setHeight(size);
39813         }
39814     
39815     },
39816     
39817     getBox : function(){
39818         if(this.collapsed){
39819             return this.collapsedEl.getBox();
39820         }
39821         var box = this.el.getBox();
39822         if(this.split){
39823             var sh = this.split.el.getHeight();
39824             box.height += sh;
39825             box.y -= sh;
39826         }
39827         return box;
39828     },
39829     
39830     updateBox : function(box){
39831         if(this.split && !this.collapsed){
39832             var sh = this.split.el.getHeight();
39833             box.height -= sh;
39834             box.y += sh;
39835             this.split.el.setLeft(box.x);
39836             this.split.el.setTop(box.y-sh);
39837             this.split.el.setWidth(box.width);
39838         }
39839         if(this.collapsed){
39840             this.updateBody(box.width, null);
39841         }
39842         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39843     }
39844 });
39845
39846 Roo.bootstrap.layout.East = function(config){
39847     config.region = "east";
39848     config.cursor = "e-resize";
39849     Roo.bootstrap.layout.Split.call(this, config);
39850     if(this.split){
39851         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39852         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39853         this.split.el.addClass("roo-layout-split-h");
39854     }
39855     
39856 };
39857 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39858     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39859     
39860     onRender : function(ctr, pos)
39861     {
39862         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39863         var size = this.config.initialSize || this.config.width;
39864         if(this.el && typeof size != "undefined"){
39865             this.el.setWidth(size);
39866         }
39867     
39868     },
39869     
39870     getBox : function(){
39871         if(this.collapsed){
39872             return this.collapsedEl.getBox();
39873         }
39874         var box = this.el.getBox();
39875         if(this.split){
39876             var sw = this.split.el.getWidth();
39877             box.width += sw;
39878             box.x -= sw;
39879         }
39880         return box;
39881     },
39882
39883     updateBox : function(box){
39884         if(this.split && !this.collapsed){
39885             var sw = this.split.el.getWidth();
39886             box.width -= sw;
39887             this.split.el.setLeft(box.x);
39888             this.split.el.setTop(box.y);
39889             this.split.el.setHeight(box.height);
39890             box.x += sw;
39891         }
39892         if(this.collapsed){
39893             this.updateBody(null, box.height);
39894         }
39895         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39896     }
39897 });
39898
39899 Roo.bootstrap.layout.West = function(config){
39900     config.region = "west";
39901     config.cursor = "w-resize";
39902     
39903     Roo.bootstrap.layout.Split.call(this, config);
39904     if(this.split){
39905         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39906         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39907         this.split.el.addClass("roo-layout-split-h");
39908     }
39909     
39910 };
39911 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39912     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39913     
39914     onRender: function(ctr, pos)
39915     {
39916         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39917         var size = this.config.initialSize || this.config.width;
39918         if(typeof size != "undefined"){
39919             this.el.setWidth(size);
39920         }
39921     },
39922     
39923     getBox : function(){
39924         if(this.collapsed){
39925             return this.collapsedEl.getBox();
39926         }
39927         var box = this.el.getBox();
39928         if (box.width == 0) {
39929             box.width = this.config.width; // kludge?
39930         }
39931         if(this.split){
39932             box.width += this.split.el.getWidth();
39933         }
39934         return box;
39935     },
39936     
39937     updateBox : function(box){
39938         if(this.split && !this.collapsed){
39939             var sw = this.split.el.getWidth();
39940             box.width -= sw;
39941             this.split.el.setLeft(box.x+box.width);
39942             this.split.el.setTop(box.y);
39943             this.split.el.setHeight(box.height);
39944         }
39945         if(this.collapsed){
39946             this.updateBody(null, box.height);
39947         }
39948         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39949     }
39950 });Roo.namespace("Roo.bootstrap.panel");/*
39951  * Based on:
39952  * Ext JS Library 1.1.1
39953  * Copyright(c) 2006-2007, Ext JS, LLC.
39954  *
39955  * Originally Released Under LGPL - original licence link has changed is not relivant.
39956  *
39957  * Fork - LGPL
39958  * <script type="text/javascript">
39959  */
39960 /**
39961  * @class Roo.ContentPanel
39962  * @extends Roo.util.Observable
39963  * A basic ContentPanel element.
39964  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39965  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39966  * @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
39967  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39968  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39969  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39970  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39971  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39972  * @cfg {String} title          The title for this panel
39973  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39974  * @cfg {String} url            Calls {@link #setUrl} with this value
39975  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39976  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39977  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39978  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39979  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39980  * @cfg {Boolean} badges render the badges
39981  * @cfg {String} cls  extra classes to use  
39982  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39983
39984  * @constructor
39985  * Create a new ContentPanel.
39986  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39987  * @param {String/Object} config A string to set only the title or a config object
39988  * @param {String} content (optional) Set the HTML content for this panel
39989  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39990  */
39991 Roo.bootstrap.panel.Content = function( config){
39992     
39993     this.tpl = config.tpl || false;
39994     
39995     var el = config.el;
39996     var content = config.content;
39997
39998     if(config.autoCreate){ // xtype is available if this is called from factory
39999         el = Roo.id();
40000     }
40001     this.el = Roo.get(el);
40002     if(!this.el && config && config.autoCreate){
40003         if(typeof config.autoCreate == "object"){
40004             if(!config.autoCreate.id){
40005                 config.autoCreate.id = config.id||el;
40006             }
40007             this.el = Roo.DomHelper.append(document.body,
40008                         config.autoCreate, true);
40009         }else{
40010             var elcfg =  {
40011                 tag: "div",
40012                 cls: (config.cls || '') +
40013                     (config.background ? ' bg-' + config.background : '') +
40014                     " roo-layout-inactive-content",
40015                 id: config.id||el
40016             };
40017             if (config.iframe) {
40018                 elcfg.cn = [
40019                     {
40020                         tag : 'iframe',
40021                         style : 'border: 0px',
40022                         src : 'about:blank'
40023                     }
40024                 ];
40025             }
40026               
40027             if (config.html) {
40028                 elcfg.html = config.html;
40029                 
40030             }
40031                         
40032             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40033             if (config.iframe) {
40034                 this.iframeEl = this.el.select('iframe',true).first();
40035             }
40036             
40037         }
40038     } 
40039     this.closable = false;
40040     this.loaded = false;
40041     this.active = false;
40042    
40043       
40044     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40045         
40046         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40047         
40048         this.wrapEl = this.el; //this.el.wrap();
40049         var ti = [];
40050         if (config.toolbar.items) {
40051             ti = config.toolbar.items ;
40052             delete config.toolbar.items ;
40053         }
40054         
40055         var nitems = [];
40056         this.toolbar.render(this.wrapEl, 'before');
40057         for(var i =0;i < ti.length;i++) {
40058           //  Roo.log(['add child', items[i]]);
40059             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40060         }
40061         this.toolbar.items = nitems;
40062         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40063         delete config.toolbar;
40064         
40065     }
40066     /*
40067     // xtype created footer. - not sure if will work as we normally have to render first..
40068     if (this.footer && !this.footer.el && this.footer.xtype) {
40069         if (!this.wrapEl) {
40070             this.wrapEl = this.el.wrap();
40071         }
40072     
40073         this.footer.container = this.wrapEl.createChild();
40074          
40075         this.footer = Roo.factory(this.footer, Roo);
40076         
40077     }
40078     */
40079     
40080      if(typeof config == "string"){
40081         this.title = config;
40082     }else{
40083         Roo.apply(this, config);
40084     }
40085     
40086     if(this.resizeEl){
40087         this.resizeEl = Roo.get(this.resizeEl, true);
40088     }else{
40089         this.resizeEl = this.el;
40090     }
40091     // handle view.xtype
40092     
40093  
40094     
40095     
40096     this.addEvents({
40097         /**
40098          * @event activate
40099          * Fires when this panel is activated. 
40100          * @param {Roo.ContentPanel} this
40101          */
40102         "activate" : true,
40103         /**
40104          * @event deactivate
40105          * Fires when this panel is activated. 
40106          * @param {Roo.ContentPanel} this
40107          */
40108         "deactivate" : true,
40109
40110         /**
40111          * @event resize
40112          * Fires when this panel is resized if fitToFrame is true.
40113          * @param {Roo.ContentPanel} this
40114          * @param {Number} width The width after any component adjustments
40115          * @param {Number} height The height after any component adjustments
40116          */
40117         "resize" : true,
40118         
40119          /**
40120          * @event render
40121          * Fires when this tab is created
40122          * @param {Roo.ContentPanel} this
40123          */
40124         "render" : true
40125         
40126         
40127         
40128     });
40129     
40130
40131     
40132     
40133     if(this.autoScroll && !this.iframe){
40134         this.resizeEl.setStyle("overflow", "auto");
40135     } else {
40136         // fix randome scrolling
40137         //this.el.on('scroll', function() {
40138         //    Roo.log('fix random scolling');
40139         //    this.scrollTo('top',0); 
40140         //});
40141     }
40142     content = content || this.content;
40143     if(content){
40144         this.setContent(content);
40145     }
40146     if(config && config.url){
40147         this.setUrl(this.url, this.params, this.loadOnce);
40148     }
40149     
40150     
40151     
40152     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40153     
40154     if (this.view && typeof(this.view.xtype) != 'undefined') {
40155         this.view.el = this.el.appendChild(document.createElement("div"));
40156         this.view = Roo.factory(this.view); 
40157         this.view.render  &&  this.view.render(false, '');  
40158     }
40159     
40160     
40161     this.fireEvent('render', this);
40162 };
40163
40164 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40165     
40166     cls : '',
40167     background : '',
40168     
40169     tabTip : '',
40170     
40171     iframe : false,
40172     iframeEl : false,
40173     
40174     setRegion : function(region){
40175         this.region = region;
40176         this.setActiveClass(region && !this.background);
40177     },
40178     
40179     
40180     setActiveClass: function(state)
40181     {
40182         if(state){
40183            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40184            this.el.setStyle('position','relative');
40185         }else{
40186            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40187            this.el.setStyle('position', 'absolute');
40188         } 
40189     },
40190     
40191     /**
40192      * Returns the toolbar for this Panel if one was configured. 
40193      * @return {Roo.Toolbar} 
40194      */
40195     getToolbar : function(){
40196         return this.toolbar;
40197     },
40198     
40199     setActiveState : function(active)
40200     {
40201         this.active = active;
40202         this.setActiveClass(active);
40203         if(!active){
40204             if(this.fireEvent("deactivate", this) === false){
40205                 return false;
40206             }
40207             return true;
40208         }
40209         this.fireEvent("activate", this);
40210         return true;
40211     },
40212     /**
40213      * Updates this panel's element (not for iframe)
40214      * @param {String} content The new content
40215      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40216     */
40217     setContent : function(content, loadScripts){
40218         if (this.iframe) {
40219             return;
40220         }
40221         
40222         this.el.update(content, loadScripts);
40223     },
40224
40225     ignoreResize : function(w, h){
40226         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40227             return true;
40228         }else{
40229             this.lastSize = {width: w, height: h};
40230             return false;
40231         }
40232     },
40233     /**
40234      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40235      * @return {Roo.UpdateManager} The UpdateManager
40236      */
40237     getUpdateManager : function(){
40238         if (this.iframe) {
40239             return false;
40240         }
40241         return this.el.getUpdateManager();
40242     },
40243      /**
40244      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40245      * Does not work with IFRAME contents
40246      * @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:
40247 <pre><code>
40248 panel.load({
40249     url: "your-url.php",
40250     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40251     callback: yourFunction,
40252     scope: yourObject, //(optional scope)
40253     discardUrl: false,
40254     nocache: false,
40255     text: "Loading...",
40256     timeout: 30,
40257     scripts: false
40258 });
40259 </code></pre>
40260      
40261      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40262      * 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.
40263      * @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}
40264      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40265      * @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.
40266      * @return {Roo.ContentPanel} this
40267      */
40268     load : function(){
40269         
40270         if (this.iframe) {
40271             return this;
40272         }
40273         
40274         var um = this.el.getUpdateManager();
40275         um.update.apply(um, arguments);
40276         return this;
40277     },
40278
40279
40280     /**
40281      * 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.
40282      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40283      * @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)
40284      * @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)
40285      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40286      */
40287     setUrl : function(url, params, loadOnce){
40288         if (this.iframe) {
40289             this.iframeEl.dom.src = url;
40290             return false;
40291         }
40292         
40293         if(this.refreshDelegate){
40294             this.removeListener("activate", this.refreshDelegate);
40295         }
40296         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40297         this.on("activate", this.refreshDelegate);
40298         return this.el.getUpdateManager();
40299     },
40300     
40301     _handleRefresh : function(url, params, loadOnce){
40302         if(!loadOnce || !this.loaded){
40303             var updater = this.el.getUpdateManager();
40304             updater.update(url, params, this._setLoaded.createDelegate(this));
40305         }
40306     },
40307     
40308     _setLoaded : function(){
40309         this.loaded = true;
40310     }, 
40311     
40312     /**
40313      * Returns this panel's id
40314      * @return {String} 
40315      */
40316     getId : function(){
40317         return this.el.id;
40318     },
40319     
40320     /** 
40321      * Returns this panel's element - used by regiosn to add.
40322      * @return {Roo.Element} 
40323      */
40324     getEl : function(){
40325         return this.wrapEl || this.el;
40326     },
40327     
40328    
40329     
40330     adjustForComponents : function(width, height)
40331     {
40332         //Roo.log('adjustForComponents ');
40333         if(this.resizeEl != this.el){
40334             width -= this.el.getFrameWidth('lr');
40335             height -= this.el.getFrameWidth('tb');
40336         }
40337         if(this.toolbar){
40338             var te = this.toolbar.getEl();
40339             te.setWidth(width);
40340             height -= te.getHeight();
40341         }
40342         if(this.footer){
40343             var te = this.footer.getEl();
40344             te.setWidth(width);
40345             height -= te.getHeight();
40346         }
40347         
40348         
40349         if(this.adjustments){
40350             width += this.adjustments[0];
40351             height += this.adjustments[1];
40352         }
40353         return {"width": width, "height": height};
40354     },
40355     
40356     setSize : function(width, height){
40357         if(this.fitToFrame && !this.ignoreResize(width, height)){
40358             if(this.fitContainer && this.resizeEl != this.el){
40359                 this.el.setSize(width, height);
40360             }
40361             var size = this.adjustForComponents(width, height);
40362             if (this.iframe) {
40363                 this.iframeEl.setSize(width,height);
40364             }
40365             
40366             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40367             this.fireEvent('resize', this, size.width, size.height);
40368             
40369             
40370         }
40371     },
40372     
40373     /**
40374      * Returns this panel's title
40375      * @return {String} 
40376      */
40377     getTitle : function(){
40378         
40379         if (typeof(this.title) != 'object') {
40380             return this.title;
40381         }
40382         
40383         var t = '';
40384         for (var k in this.title) {
40385             if (!this.title.hasOwnProperty(k)) {
40386                 continue;
40387             }
40388             
40389             if (k.indexOf('-') >= 0) {
40390                 var s = k.split('-');
40391                 for (var i = 0; i<s.length; i++) {
40392                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40393                 }
40394             } else {
40395                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40396             }
40397         }
40398         return t;
40399     },
40400     
40401     /**
40402      * Set this panel's title
40403      * @param {String} title
40404      */
40405     setTitle : function(title){
40406         this.title = title;
40407         if(this.region){
40408             this.region.updatePanelTitle(this, title);
40409         }
40410     },
40411     
40412     /**
40413      * Returns true is this panel was configured to be closable
40414      * @return {Boolean} 
40415      */
40416     isClosable : function(){
40417         return this.closable;
40418     },
40419     
40420     beforeSlide : function(){
40421         this.el.clip();
40422         this.resizeEl.clip();
40423     },
40424     
40425     afterSlide : function(){
40426         this.el.unclip();
40427         this.resizeEl.unclip();
40428     },
40429     
40430     /**
40431      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40432      *   Will fail silently if the {@link #setUrl} method has not been called.
40433      *   This does not activate the panel, just updates its content.
40434      */
40435     refresh : function(){
40436         if(this.refreshDelegate){
40437            this.loaded = false;
40438            this.refreshDelegate();
40439         }
40440     },
40441     
40442     /**
40443      * Destroys this panel
40444      */
40445     destroy : function(){
40446         this.el.removeAllListeners();
40447         var tempEl = document.createElement("span");
40448         tempEl.appendChild(this.el.dom);
40449         tempEl.innerHTML = "";
40450         this.el.remove();
40451         this.el = null;
40452     },
40453     
40454     /**
40455      * form - if the content panel contains a form - this is a reference to it.
40456      * @type {Roo.form.Form}
40457      */
40458     form : false,
40459     /**
40460      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40461      *    This contains a reference to it.
40462      * @type {Roo.View}
40463      */
40464     view : false,
40465     
40466       /**
40467      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40468      * <pre><code>
40469
40470 layout.addxtype({
40471        xtype : 'Form',
40472        items: [ .... ]
40473    }
40474 );
40475
40476 </code></pre>
40477      * @param {Object} cfg Xtype definition of item to add.
40478      */
40479     
40480     
40481     getChildContainer: function () {
40482         return this.getEl();
40483     }
40484     
40485     
40486     /*
40487         var  ret = new Roo.factory(cfg);
40488         return ret;
40489         
40490         
40491         // add form..
40492         if (cfg.xtype.match(/^Form$/)) {
40493             
40494             var el;
40495             //if (this.footer) {
40496             //    el = this.footer.container.insertSibling(false, 'before');
40497             //} else {
40498                 el = this.el.createChild();
40499             //}
40500
40501             this.form = new  Roo.form.Form(cfg);
40502             
40503             
40504             if ( this.form.allItems.length) {
40505                 this.form.render(el.dom);
40506             }
40507             return this.form;
40508         }
40509         // should only have one of theses..
40510         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40511             // views.. should not be just added - used named prop 'view''
40512             
40513             cfg.el = this.el.appendChild(document.createElement("div"));
40514             // factory?
40515             
40516             var ret = new Roo.factory(cfg);
40517              
40518              ret.render && ret.render(false, ''); // render blank..
40519             this.view = ret;
40520             return ret;
40521         }
40522         return false;
40523     }
40524     \*/
40525 });
40526  
40527 /**
40528  * @class Roo.bootstrap.panel.Grid
40529  * @extends Roo.bootstrap.panel.Content
40530  * @constructor
40531  * Create a new GridPanel.
40532  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40533  * @param {Object} config A the config object
40534   
40535  */
40536
40537
40538
40539 Roo.bootstrap.panel.Grid = function(config)
40540 {
40541     
40542       
40543     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40544         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40545
40546     config.el = this.wrapper;
40547     //this.el = this.wrapper;
40548     
40549       if (config.container) {
40550         // ctor'ed from a Border/panel.grid
40551         
40552         
40553         this.wrapper.setStyle("overflow", "hidden");
40554         this.wrapper.addClass('roo-grid-container');
40555
40556     }
40557     
40558     
40559     if(config.toolbar){
40560         var tool_el = this.wrapper.createChild();    
40561         this.toolbar = Roo.factory(config.toolbar);
40562         var ti = [];
40563         if (config.toolbar.items) {
40564             ti = config.toolbar.items ;
40565             delete config.toolbar.items ;
40566         }
40567         
40568         var nitems = [];
40569         this.toolbar.render(tool_el);
40570         for(var i =0;i < ti.length;i++) {
40571           //  Roo.log(['add child', items[i]]);
40572             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40573         }
40574         this.toolbar.items = nitems;
40575         
40576         delete config.toolbar;
40577     }
40578     
40579     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40580     config.grid.scrollBody = true;;
40581     config.grid.monitorWindowResize = false; // turn off autosizing
40582     config.grid.autoHeight = false;
40583     config.grid.autoWidth = false;
40584     
40585     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40586     
40587     if (config.background) {
40588         // render grid on panel activation (if panel background)
40589         this.on('activate', function(gp) {
40590             if (!gp.grid.rendered) {
40591                 gp.grid.render(this.wrapper);
40592                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40593             }
40594         });
40595             
40596     } else {
40597         this.grid.render(this.wrapper);
40598         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40599
40600     }
40601     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40602     // ??? needed ??? config.el = this.wrapper;
40603     
40604     
40605     
40606   
40607     // xtype created footer. - not sure if will work as we normally have to render first..
40608     if (this.footer && !this.footer.el && this.footer.xtype) {
40609         
40610         var ctr = this.grid.getView().getFooterPanel(true);
40611         this.footer.dataSource = this.grid.dataSource;
40612         this.footer = Roo.factory(this.footer, Roo);
40613         this.footer.render(ctr);
40614         
40615     }
40616     
40617     
40618     
40619     
40620      
40621 };
40622
40623 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40624     getId : function(){
40625         return this.grid.id;
40626     },
40627     
40628     /**
40629      * Returns the grid for this panel
40630      * @return {Roo.bootstrap.Table} 
40631      */
40632     getGrid : function(){
40633         return this.grid;    
40634     },
40635     
40636     setSize : function(width, height){
40637         if(!this.ignoreResize(width, height)){
40638             var grid = this.grid;
40639             var size = this.adjustForComponents(width, height);
40640             // tfoot is not a footer?
40641           
40642             
40643             var gridel = grid.getGridEl();
40644             gridel.setSize(size.width, size.height);
40645             
40646             var tbd = grid.getGridEl().select('tbody', true).first();
40647             var thd = grid.getGridEl().select('thead',true).first();
40648             var tbf= grid.getGridEl().select('tfoot', true).first();
40649
40650             if (tbf) {
40651                 size.height -= tbf.getHeight();
40652             }
40653             if (thd) {
40654                 size.height -= thd.getHeight();
40655             }
40656             
40657             tbd.setSize(size.width, size.height );
40658             // this is for the account management tab -seems to work there.
40659             var thd = grid.getGridEl().select('thead',true).first();
40660             //if (tbd) {
40661             //    tbd.setSize(size.width, size.height - thd.getHeight());
40662             //}
40663              
40664             grid.autoSize();
40665         }
40666     },
40667      
40668     
40669     
40670     beforeSlide : function(){
40671         this.grid.getView().scroller.clip();
40672     },
40673     
40674     afterSlide : function(){
40675         this.grid.getView().scroller.unclip();
40676     },
40677     
40678     destroy : function(){
40679         this.grid.destroy();
40680         delete this.grid;
40681         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40682     }
40683 });
40684
40685 /**
40686  * @class Roo.bootstrap.panel.Nest
40687  * @extends Roo.bootstrap.panel.Content
40688  * @constructor
40689  * Create a new Panel, that can contain a layout.Border.
40690  * 
40691  * 
40692  * @param {Roo.BorderLayout} layout The layout for this panel
40693  * @param {String/Object} config A string to set only the title or a config object
40694  */
40695 Roo.bootstrap.panel.Nest = function(config)
40696 {
40697     // construct with only one argument..
40698     /* FIXME - implement nicer consturctors
40699     if (layout.layout) {
40700         config = layout;
40701         layout = config.layout;
40702         delete config.layout;
40703     }
40704     if (layout.xtype && !layout.getEl) {
40705         // then layout needs constructing..
40706         layout = Roo.factory(layout, Roo);
40707     }
40708     */
40709     
40710     config.el =  config.layout.getEl();
40711     
40712     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40713     
40714     config.layout.monitorWindowResize = false; // turn off autosizing
40715     this.layout = config.layout;
40716     this.layout.getEl().addClass("roo-layout-nested-layout");
40717     this.layout.parent = this;
40718     
40719     
40720     
40721     
40722 };
40723
40724 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40725
40726     setSize : function(width, height){
40727         if(!this.ignoreResize(width, height)){
40728             var size = this.adjustForComponents(width, height);
40729             var el = this.layout.getEl();
40730             if (size.height < 1) {
40731                 el.setWidth(size.width);   
40732             } else {
40733                 el.setSize(size.width, size.height);
40734             }
40735             var touch = el.dom.offsetWidth;
40736             this.layout.layout();
40737             // ie requires a double layout on the first pass
40738             if(Roo.isIE && !this.initialized){
40739                 this.initialized = true;
40740                 this.layout.layout();
40741             }
40742         }
40743     },
40744     
40745     // activate all subpanels if not currently active..
40746     
40747     setActiveState : function(active){
40748         this.active = active;
40749         this.setActiveClass(active);
40750         
40751         if(!active){
40752             this.fireEvent("deactivate", this);
40753             return;
40754         }
40755         
40756         this.fireEvent("activate", this);
40757         // not sure if this should happen before or after..
40758         if (!this.layout) {
40759             return; // should not happen..
40760         }
40761         var reg = false;
40762         for (var r in this.layout.regions) {
40763             reg = this.layout.getRegion(r);
40764             if (reg.getActivePanel()) {
40765                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40766                 reg.setActivePanel(reg.getActivePanel());
40767                 continue;
40768             }
40769             if (!reg.panels.length) {
40770                 continue;
40771             }
40772             reg.showPanel(reg.getPanel(0));
40773         }
40774         
40775         
40776         
40777         
40778     },
40779     
40780     /**
40781      * Returns the nested BorderLayout for this panel
40782      * @return {Roo.BorderLayout} 
40783      */
40784     getLayout : function(){
40785         return this.layout;
40786     },
40787     
40788      /**
40789      * Adds a xtype elements to the layout of the nested panel
40790      * <pre><code>
40791
40792 panel.addxtype({
40793        xtype : 'ContentPanel',
40794        region: 'west',
40795        items: [ .... ]
40796    }
40797 );
40798
40799 panel.addxtype({
40800         xtype : 'NestedLayoutPanel',
40801         region: 'west',
40802         layout: {
40803            center: { },
40804            west: { }   
40805         },
40806         items : [ ... list of content panels or nested layout panels.. ]
40807    }
40808 );
40809 </code></pre>
40810      * @param {Object} cfg Xtype definition of item to add.
40811      */
40812     addxtype : function(cfg) {
40813         return this.layout.addxtype(cfg);
40814     
40815     }
40816 });/*
40817  * Based on:
40818  * Ext JS Library 1.1.1
40819  * Copyright(c) 2006-2007, Ext JS, LLC.
40820  *
40821  * Originally Released Under LGPL - original licence link has changed is not relivant.
40822  *
40823  * Fork - LGPL
40824  * <script type="text/javascript">
40825  */
40826 /**
40827  * @class Roo.TabPanel
40828  * @extends Roo.util.Observable
40829  * A lightweight tab container.
40830  * <br><br>
40831  * Usage:
40832  * <pre><code>
40833 // basic tabs 1, built from existing content
40834 var tabs = new Roo.TabPanel("tabs1");
40835 tabs.addTab("script", "View Script");
40836 tabs.addTab("markup", "View Markup");
40837 tabs.activate("script");
40838
40839 // more advanced tabs, built from javascript
40840 var jtabs = new Roo.TabPanel("jtabs");
40841 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40842
40843 // set up the UpdateManager
40844 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40845 var updater = tab2.getUpdateManager();
40846 updater.setDefaultUrl("ajax1.htm");
40847 tab2.on('activate', updater.refresh, updater, true);
40848
40849 // Use setUrl for Ajax loading
40850 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40851 tab3.setUrl("ajax2.htm", null, true);
40852
40853 // Disabled tab
40854 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40855 tab4.disable();
40856
40857 jtabs.activate("jtabs-1");
40858  * </code></pre>
40859  * @constructor
40860  * Create a new TabPanel.
40861  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40862  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40863  */
40864 Roo.bootstrap.panel.Tabs = function(config){
40865     /**
40866     * The container element for this TabPanel.
40867     * @type Roo.Element
40868     */
40869     this.el = Roo.get(config.el);
40870     delete config.el;
40871     if(config){
40872         if(typeof config == "boolean"){
40873             this.tabPosition = config ? "bottom" : "top";
40874         }else{
40875             Roo.apply(this, config);
40876         }
40877     }
40878     
40879     if(this.tabPosition == "bottom"){
40880         // if tabs are at the bottom = create the body first.
40881         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40882         this.el.addClass("roo-tabs-bottom");
40883     }
40884     // next create the tabs holders
40885     
40886     if (this.tabPosition == "west"){
40887         
40888         var reg = this.region; // fake it..
40889         while (reg) {
40890             if (!reg.mgr.parent) {
40891                 break;
40892             }
40893             reg = reg.mgr.parent.region;
40894         }
40895         Roo.log("got nest?");
40896         Roo.log(reg);
40897         if (reg.mgr.getRegion('west')) {
40898             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40899             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40900             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40901             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40902             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40903         
40904             
40905         }
40906         
40907         
40908     } else {
40909      
40910         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40911         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40912         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40913         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40914     }
40915     
40916     
40917     if(Roo.isIE){
40918         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40919     }
40920     
40921     // finally - if tabs are at the top, then create the body last..
40922     if(this.tabPosition != "bottom"){
40923         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40924          * @type Roo.Element
40925          */
40926         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40927         this.el.addClass("roo-tabs-top");
40928     }
40929     this.items = [];
40930
40931     this.bodyEl.setStyle("position", "relative");
40932
40933     this.active = null;
40934     this.activateDelegate = this.activate.createDelegate(this);
40935
40936     this.addEvents({
40937         /**
40938          * @event tabchange
40939          * Fires when the active tab changes
40940          * @param {Roo.TabPanel} this
40941          * @param {Roo.TabPanelItem} activePanel The new active tab
40942          */
40943         "tabchange": true,
40944         /**
40945          * @event beforetabchange
40946          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40947          * @param {Roo.TabPanel} this
40948          * @param {Object} e Set cancel to true on this object to cancel the tab change
40949          * @param {Roo.TabPanelItem} tab The tab being changed to
40950          */
40951         "beforetabchange" : true
40952     });
40953
40954     Roo.EventManager.onWindowResize(this.onResize, this);
40955     this.cpad = this.el.getPadding("lr");
40956     this.hiddenCount = 0;
40957
40958
40959     // toolbar on the tabbar support...
40960     if (this.toolbar) {
40961         alert("no toolbar support yet");
40962         this.toolbar  = false;
40963         /*
40964         var tcfg = this.toolbar;
40965         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40966         this.toolbar = new Roo.Toolbar(tcfg);
40967         if (Roo.isSafari) {
40968             var tbl = tcfg.container.child('table', true);
40969             tbl.setAttribute('width', '100%');
40970         }
40971         */
40972         
40973     }
40974    
40975
40976
40977     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40978 };
40979
40980 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40981     /*
40982      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40983      */
40984     tabPosition : "top",
40985     /*
40986      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40987      */
40988     currentTabWidth : 0,
40989     /*
40990      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40991      */
40992     minTabWidth : 40,
40993     /*
40994      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40995      */
40996     maxTabWidth : 250,
40997     /*
40998      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40999      */
41000     preferredTabWidth : 175,
41001     /*
41002      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41003      */
41004     resizeTabs : false,
41005     /*
41006      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41007      */
41008     monitorResize : true,
41009     /*
41010      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41011      */
41012     toolbar : false,  // set by caller..
41013     
41014     region : false, /// set by caller
41015     
41016     disableTooltips : true, // not used yet...
41017
41018     /**
41019      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41020      * @param {String} id The id of the div to use <b>or create</b>
41021      * @param {String} text The text for the tab
41022      * @param {String} content (optional) Content to put in the TabPanelItem body
41023      * @param {Boolean} closable (optional) True to create a close icon on the tab
41024      * @return {Roo.TabPanelItem} The created TabPanelItem
41025      */
41026     addTab : function(id, text, content, closable, tpl)
41027     {
41028         var item = new Roo.bootstrap.panel.TabItem({
41029             panel: this,
41030             id : id,
41031             text : text,
41032             closable : closable,
41033             tpl : tpl
41034         });
41035         this.addTabItem(item);
41036         if(content){
41037             item.setContent(content);
41038         }
41039         return item;
41040     },
41041
41042     /**
41043      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41044      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41045      * @return {Roo.TabPanelItem}
41046      */
41047     getTab : function(id){
41048         return this.items[id];
41049     },
41050
41051     /**
41052      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41053      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41054      */
41055     hideTab : function(id){
41056         var t = this.items[id];
41057         if(!t.isHidden()){
41058            t.setHidden(true);
41059            this.hiddenCount++;
41060            this.autoSizeTabs();
41061         }
41062     },
41063
41064     /**
41065      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41066      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41067      */
41068     unhideTab : function(id){
41069         var t = this.items[id];
41070         if(t.isHidden()){
41071            t.setHidden(false);
41072            this.hiddenCount--;
41073            this.autoSizeTabs();
41074         }
41075     },
41076
41077     /**
41078      * Adds an existing {@link Roo.TabPanelItem}.
41079      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41080      */
41081     addTabItem : function(item)
41082     {
41083         this.items[item.id] = item;
41084         this.items.push(item);
41085         this.autoSizeTabs();
41086       //  if(this.resizeTabs){
41087     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41088   //         this.autoSizeTabs();
41089 //        }else{
41090 //            item.autoSize();
41091        // }
41092     },
41093
41094     /**
41095      * Removes a {@link Roo.TabPanelItem}.
41096      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41097      */
41098     removeTab : function(id){
41099         var items = this.items;
41100         var tab = items[id];
41101         if(!tab) { return; }
41102         var index = items.indexOf(tab);
41103         if(this.active == tab && items.length > 1){
41104             var newTab = this.getNextAvailable(index);
41105             if(newTab) {
41106                 newTab.activate();
41107             }
41108         }
41109         this.stripEl.dom.removeChild(tab.pnode.dom);
41110         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41111             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41112         }
41113         items.splice(index, 1);
41114         delete this.items[tab.id];
41115         tab.fireEvent("close", tab);
41116         tab.purgeListeners();
41117         this.autoSizeTabs();
41118     },
41119
41120     getNextAvailable : function(start){
41121         var items = this.items;
41122         var index = start;
41123         // look for a next tab that will slide over to
41124         // replace the one being removed
41125         while(index < items.length){
41126             var item = items[++index];
41127             if(item && !item.isHidden()){
41128                 return item;
41129             }
41130         }
41131         // if one isn't found select the previous tab (on the left)
41132         index = start;
41133         while(index >= 0){
41134             var item = items[--index];
41135             if(item && !item.isHidden()){
41136                 return item;
41137             }
41138         }
41139         return null;
41140     },
41141
41142     /**
41143      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41144      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41145      */
41146     disableTab : function(id){
41147         var tab = this.items[id];
41148         if(tab && this.active != tab){
41149             tab.disable();
41150         }
41151     },
41152
41153     /**
41154      * Enables a {@link Roo.TabPanelItem} that is disabled.
41155      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41156      */
41157     enableTab : function(id){
41158         var tab = this.items[id];
41159         tab.enable();
41160     },
41161
41162     /**
41163      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41164      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41165      * @return {Roo.TabPanelItem} The TabPanelItem.
41166      */
41167     activate : function(id)
41168     {
41169         //Roo.log('activite:'  + id);
41170         
41171         var tab = this.items[id];
41172         if(!tab){
41173             return null;
41174         }
41175         if(tab == this.active || tab.disabled){
41176             return tab;
41177         }
41178         var e = {};
41179         this.fireEvent("beforetabchange", this, e, tab);
41180         if(e.cancel !== true && !tab.disabled){
41181             if(this.active){
41182                 this.active.hide();
41183             }
41184             this.active = this.items[id];
41185             this.active.show();
41186             this.fireEvent("tabchange", this, this.active);
41187         }
41188         return tab;
41189     },
41190
41191     /**
41192      * Gets the active {@link Roo.TabPanelItem}.
41193      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41194      */
41195     getActiveTab : function(){
41196         return this.active;
41197     },
41198
41199     /**
41200      * Updates the tab body element to fit the height of the container element
41201      * for overflow scrolling
41202      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41203      */
41204     syncHeight : function(targetHeight){
41205         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41206         var bm = this.bodyEl.getMargins();
41207         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41208         this.bodyEl.setHeight(newHeight);
41209         return newHeight;
41210     },
41211
41212     onResize : function(){
41213         if(this.monitorResize){
41214             this.autoSizeTabs();
41215         }
41216     },
41217
41218     /**
41219      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41220      */
41221     beginUpdate : function(){
41222         this.updating = true;
41223     },
41224
41225     /**
41226      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41227      */
41228     endUpdate : function(){
41229         this.updating = false;
41230         this.autoSizeTabs();
41231     },
41232
41233     /**
41234      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41235      */
41236     autoSizeTabs : function()
41237     {
41238         var count = this.items.length;
41239         var vcount = count - this.hiddenCount;
41240         
41241         if (vcount < 2) {
41242             this.stripEl.hide();
41243         } else {
41244             this.stripEl.show();
41245         }
41246         
41247         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41248             return;
41249         }
41250         
41251         
41252         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41253         var availWidth = Math.floor(w / vcount);
41254         var b = this.stripBody;
41255         if(b.getWidth() > w){
41256             var tabs = this.items;
41257             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41258             if(availWidth < this.minTabWidth){
41259                 /*if(!this.sleft){    // incomplete scrolling code
41260                     this.createScrollButtons();
41261                 }
41262                 this.showScroll();
41263                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41264             }
41265         }else{
41266             if(this.currentTabWidth < this.preferredTabWidth){
41267                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41268             }
41269         }
41270     },
41271
41272     /**
41273      * Returns the number of tabs in this TabPanel.
41274      * @return {Number}
41275      */
41276      getCount : function(){
41277          return this.items.length;
41278      },
41279
41280     /**
41281      * Resizes all the tabs to the passed width
41282      * @param {Number} The new width
41283      */
41284     setTabWidth : function(width){
41285         this.currentTabWidth = width;
41286         for(var i = 0, len = this.items.length; i < len; i++) {
41287                 if(!this.items[i].isHidden()) {
41288                 this.items[i].setWidth(width);
41289             }
41290         }
41291     },
41292
41293     /**
41294      * Destroys this TabPanel
41295      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41296      */
41297     destroy : function(removeEl){
41298         Roo.EventManager.removeResizeListener(this.onResize, this);
41299         for(var i = 0, len = this.items.length; i < len; i++){
41300             this.items[i].purgeListeners();
41301         }
41302         if(removeEl === true){
41303             this.el.update("");
41304             this.el.remove();
41305         }
41306     },
41307     
41308     createStrip : function(container)
41309     {
41310         var strip = document.createElement("nav");
41311         strip.className = Roo.bootstrap.version == 4 ?
41312             "navbar-light bg-light" : 
41313             "navbar navbar-default"; //"x-tabs-wrap";
41314         container.appendChild(strip);
41315         return strip;
41316     },
41317     
41318     createStripList : function(strip)
41319     {
41320         // div wrapper for retard IE
41321         // returns the "tr" element.
41322         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41323         //'<div class="x-tabs-strip-wrap">'+
41324           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41325           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41326         return strip.firstChild; //.firstChild.firstChild.firstChild;
41327     },
41328     createBody : function(container)
41329     {
41330         var body = document.createElement("div");
41331         Roo.id(body, "tab-body");
41332         //Roo.fly(body).addClass("x-tabs-body");
41333         Roo.fly(body).addClass("tab-content");
41334         container.appendChild(body);
41335         return body;
41336     },
41337     createItemBody :function(bodyEl, id){
41338         var body = Roo.getDom(id);
41339         if(!body){
41340             body = document.createElement("div");
41341             body.id = id;
41342         }
41343         //Roo.fly(body).addClass("x-tabs-item-body");
41344         Roo.fly(body).addClass("tab-pane");
41345          bodyEl.insertBefore(body, bodyEl.firstChild);
41346         return body;
41347     },
41348     /** @private */
41349     createStripElements :  function(stripEl, text, closable, tpl)
41350     {
41351         var td = document.createElement("li"); // was td..
41352         td.className = 'nav-item';
41353         
41354         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41355         
41356         
41357         stripEl.appendChild(td);
41358         /*if(closable){
41359             td.className = "x-tabs-closable";
41360             if(!this.closeTpl){
41361                 this.closeTpl = new Roo.Template(
41362                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41363                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41364                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41365                 );
41366             }
41367             var el = this.closeTpl.overwrite(td, {"text": text});
41368             var close = el.getElementsByTagName("div")[0];
41369             var inner = el.getElementsByTagName("em")[0];
41370             return {"el": el, "close": close, "inner": inner};
41371         } else {
41372         */
41373         // not sure what this is..
41374 //            if(!this.tabTpl){
41375                 //this.tabTpl = new Roo.Template(
41376                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41377                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41378                 //);
41379 //                this.tabTpl = new Roo.Template(
41380 //                   '<a href="#">' +
41381 //                   '<span unselectable="on"' +
41382 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41383 //                            ' >{text}</span></a>'
41384 //                );
41385 //                
41386 //            }
41387
41388
41389             var template = tpl || this.tabTpl || false;
41390             
41391             if(!template){
41392                 template =  new Roo.Template(
41393                         Roo.bootstrap.version == 4 ? 
41394                             (
41395                                 '<a class="nav-link" href="#" unselectable="on"' +
41396                                      (this.disableTooltips ? '' : ' title="{text}"') +
41397                                      ' >{text}</a>'
41398                             ) : (
41399                                 '<a class="nav-link" href="#">' +
41400                                 '<span unselectable="on"' +
41401                                          (this.disableTooltips ? '' : ' title="{text}"') +
41402                                     ' >{text}</span></a>'
41403                             )
41404                 );
41405             }
41406             
41407             switch (typeof(template)) {
41408                 case 'object' :
41409                     break;
41410                 case 'string' :
41411                     template = new Roo.Template(template);
41412                     break;
41413                 default :
41414                     break;
41415             }
41416             
41417             var el = template.overwrite(td, {"text": text});
41418             
41419             var inner = el.getElementsByTagName("span")[0];
41420             
41421             return {"el": el, "inner": inner};
41422             
41423     }
41424         
41425     
41426 });
41427
41428 /**
41429  * @class Roo.TabPanelItem
41430  * @extends Roo.util.Observable
41431  * Represents an individual item (tab plus body) in a TabPanel.
41432  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41433  * @param {String} id The id of this TabPanelItem
41434  * @param {String} text The text for the tab of this TabPanelItem
41435  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41436  */
41437 Roo.bootstrap.panel.TabItem = function(config){
41438     /**
41439      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41440      * @type Roo.TabPanel
41441      */
41442     this.tabPanel = config.panel;
41443     /**
41444      * The id for this TabPanelItem
41445      * @type String
41446      */
41447     this.id = config.id;
41448     /** @private */
41449     this.disabled = false;
41450     /** @private */
41451     this.text = config.text;
41452     /** @private */
41453     this.loaded = false;
41454     this.closable = config.closable;
41455
41456     /**
41457      * The body element for this TabPanelItem.
41458      * @type Roo.Element
41459      */
41460     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41461     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41462     this.bodyEl.setStyle("display", "block");
41463     this.bodyEl.setStyle("zoom", "1");
41464     //this.hideAction();
41465
41466     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41467     /** @private */
41468     this.el = Roo.get(els.el);
41469     this.inner = Roo.get(els.inner, true);
41470      this.textEl = Roo.bootstrap.version == 4 ?
41471         this.el : Roo.get(this.el.dom.firstChild, true);
41472
41473     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41474     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41475
41476     
41477 //    this.el.on("mousedown", this.onTabMouseDown, this);
41478     this.el.on("click", this.onTabClick, this);
41479     /** @private */
41480     if(config.closable){
41481         var c = Roo.get(els.close, true);
41482         c.dom.title = this.closeText;
41483         c.addClassOnOver("close-over");
41484         c.on("click", this.closeClick, this);
41485      }
41486
41487     this.addEvents({
41488          /**
41489          * @event activate
41490          * Fires when this tab becomes the active tab.
41491          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41492          * @param {Roo.TabPanelItem} this
41493          */
41494         "activate": true,
41495         /**
41496          * @event beforeclose
41497          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41498          * @param {Roo.TabPanelItem} this
41499          * @param {Object} e Set cancel to true on this object to cancel the close.
41500          */
41501         "beforeclose": true,
41502         /**
41503          * @event close
41504          * Fires when this tab is closed.
41505          * @param {Roo.TabPanelItem} this
41506          */
41507          "close": true,
41508         /**
41509          * @event deactivate
41510          * Fires when this tab is no longer the active tab.
41511          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41512          * @param {Roo.TabPanelItem} this
41513          */
41514          "deactivate" : true
41515     });
41516     this.hidden = false;
41517
41518     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41519 };
41520
41521 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41522            {
41523     purgeListeners : function(){
41524        Roo.util.Observable.prototype.purgeListeners.call(this);
41525        this.el.removeAllListeners();
41526     },
41527     /**
41528      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41529      */
41530     show : function(){
41531         this.status_node.addClass("active");
41532         this.showAction();
41533         if(Roo.isOpera){
41534             this.tabPanel.stripWrap.repaint();
41535         }
41536         this.fireEvent("activate", this.tabPanel, this);
41537     },
41538
41539     /**
41540      * Returns true if this tab is the active tab.
41541      * @return {Boolean}
41542      */
41543     isActive : function(){
41544         return this.tabPanel.getActiveTab() == this;
41545     },
41546
41547     /**
41548      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41549      */
41550     hide : function(){
41551         this.status_node.removeClass("active");
41552         this.hideAction();
41553         this.fireEvent("deactivate", this.tabPanel, this);
41554     },
41555
41556     hideAction : function(){
41557         this.bodyEl.hide();
41558         this.bodyEl.setStyle("position", "absolute");
41559         this.bodyEl.setLeft("-20000px");
41560         this.bodyEl.setTop("-20000px");
41561     },
41562
41563     showAction : function(){
41564         this.bodyEl.setStyle("position", "relative");
41565         this.bodyEl.setTop("");
41566         this.bodyEl.setLeft("");
41567         this.bodyEl.show();
41568     },
41569
41570     /**
41571      * Set the tooltip for the tab.
41572      * @param {String} tooltip The tab's tooltip
41573      */
41574     setTooltip : function(text){
41575         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41576             this.textEl.dom.qtip = text;
41577             this.textEl.dom.removeAttribute('title');
41578         }else{
41579             this.textEl.dom.title = text;
41580         }
41581     },
41582
41583     onTabClick : function(e){
41584         e.preventDefault();
41585         this.tabPanel.activate(this.id);
41586     },
41587
41588     onTabMouseDown : function(e){
41589         e.preventDefault();
41590         this.tabPanel.activate(this.id);
41591     },
41592 /*
41593     getWidth : function(){
41594         return this.inner.getWidth();
41595     },
41596
41597     setWidth : function(width){
41598         var iwidth = width - this.linode.getPadding("lr");
41599         this.inner.setWidth(iwidth);
41600         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41601         this.linode.setWidth(width);
41602     },
41603 */
41604     /**
41605      * Show or hide the tab
41606      * @param {Boolean} hidden True to hide or false to show.
41607      */
41608     setHidden : function(hidden){
41609         this.hidden = hidden;
41610         this.linode.setStyle("display", hidden ? "none" : "");
41611     },
41612
41613     /**
41614      * Returns true if this tab is "hidden"
41615      * @return {Boolean}
41616      */
41617     isHidden : function(){
41618         return this.hidden;
41619     },
41620
41621     /**
41622      * Returns the text for this tab
41623      * @return {String}
41624      */
41625     getText : function(){
41626         return this.text;
41627     },
41628     /*
41629     autoSize : function(){
41630         //this.el.beginMeasure();
41631         this.textEl.setWidth(1);
41632         /*
41633          *  #2804 [new] Tabs in Roojs
41634          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41635          */
41636         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41637         //this.el.endMeasure();
41638     //},
41639
41640     /**
41641      * Sets the text for the tab (Note: this also sets the tooltip text)
41642      * @param {String} text The tab's text and tooltip
41643      */
41644     setText : function(text){
41645         this.text = text;
41646         this.textEl.update(text);
41647         this.setTooltip(text);
41648         //if(!this.tabPanel.resizeTabs){
41649         //    this.autoSize();
41650         //}
41651     },
41652     /**
41653      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41654      */
41655     activate : function(){
41656         this.tabPanel.activate(this.id);
41657     },
41658
41659     /**
41660      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41661      */
41662     disable : function(){
41663         if(this.tabPanel.active != this){
41664             this.disabled = true;
41665             this.status_node.addClass("disabled");
41666         }
41667     },
41668
41669     /**
41670      * Enables this TabPanelItem if it was previously disabled.
41671      */
41672     enable : function(){
41673         this.disabled = false;
41674         this.status_node.removeClass("disabled");
41675     },
41676
41677     /**
41678      * Sets the content for this TabPanelItem.
41679      * @param {String} content The content
41680      * @param {Boolean} loadScripts true to look for and load scripts
41681      */
41682     setContent : function(content, loadScripts){
41683         this.bodyEl.update(content, loadScripts);
41684     },
41685
41686     /**
41687      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41688      * @return {Roo.UpdateManager} The UpdateManager
41689      */
41690     getUpdateManager : function(){
41691         return this.bodyEl.getUpdateManager();
41692     },
41693
41694     /**
41695      * Set a URL to be used to load the content for this TabPanelItem.
41696      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41697      * @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)
41698      * @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)
41699      * @return {Roo.UpdateManager} The UpdateManager
41700      */
41701     setUrl : function(url, params, loadOnce){
41702         if(this.refreshDelegate){
41703             this.un('activate', this.refreshDelegate);
41704         }
41705         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41706         this.on("activate", this.refreshDelegate);
41707         return this.bodyEl.getUpdateManager();
41708     },
41709
41710     /** @private */
41711     _handleRefresh : function(url, params, loadOnce){
41712         if(!loadOnce || !this.loaded){
41713             var updater = this.bodyEl.getUpdateManager();
41714             updater.update(url, params, this._setLoaded.createDelegate(this));
41715         }
41716     },
41717
41718     /**
41719      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41720      *   Will fail silently if the setUrl method has not been called.
41721      *   This does not activate the panel, just updates its content.
41722      */
41723     refresh : function(){
41724         if(this.refreshDelegate){
41725            this.loaded = false;
41726            this.refreshDelegate();
41727         }
41728     },
41729
41730     /** @private */
41731     _setLoaded : function(){
41732         this.loaded = true;
41733     },
41734
41735     /** @private */
41736     closeClick : function(e){
41737         var o = {};
41738         e.stopEvent();
41739         this.fireEvent("beforeclose", this, o);
41740         if(o.cancel !== true){
41741             this.tabPanel.removeTab(this.id);
41742         }
41743     },
41744     /**
41745      * The text displayed in the tooltip for the close icon.
41746      * @type String
41747      */
41748     closeText : "Close this tab"
41749 });
41750 /**
41751 *    This script refer to:
41752 *    Title: International Telephone Input
41753 *    Author: Jack O'Connor
41754 *    Code version:  v12.1.12
41755 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41756 **/
41757
41758 Roo.bootstrap.PhoneInputData = function() {
41759     var d = [
41760       [
41761         "Afghanistan (‫افغانستان‬‎)",
41762         "af",
41763         "93"
41764       ],
41765       [
41766         "Albania (Shqipëri)",
41767         "al",
41768         "355"
41769       ],
41770       [
41771         "Algeria (‫الجزائر‬‎)",
41772         "dz",
41773         "213"
41774       ],
41775       [
41776         "American Samoa",
41777         "as",
41778         "1684"
41779       ],
41780       [
41781         "Andorra",
41782         "ad",
41783         "376"
41784       ],
41785       [
41786         "Angola",
41787         "ao",
41788         "244"
41789       ],
41790       [
41791         "Anguilla",
41792         "ai",
41793         "1264"
41794       ],
41795       [
41796         "Antigua and Barbuda",
41797         "ag",
41798         "1268"
41799       ],
41800       [
41801         "Argentina",
41802         "ar",
41803         "54"
41804       ],
41805       [
41806         "Armenia (Հայաստան)",
41807         "am",
41808         "374"
41809       ],
41810       [
41811         "Aruba",
41812         "aw",
41813         "297"
41814       ],
41815       [
41816         "Australia",
41817         "au",
41818         "61",
41819         0
41820       ],
41821       [
41822         "Austria (Österreich)",
41823         "at",
41824         "43"
41825       ],
41826       [
41827         "Azerbaijan (Azərbaycan)",
41828         "az",
41829         "994"
41830       ],
41831       [
41832         "Bahamas",
41833         "bs",
41834         "1242"
41835       ],
41836       [
41837         "Bahrain (‫البحرين‬‎)",
41838         "bh",
41839         "973"
41840       ],
41841       [
41842         "Bangladesh (বাংলাদেশ)",
41843         "bd",
41844         "880"
41845       ],
41846       [
41847         "Barbados",
41848         "bb",
41849         "1246"
41850       ],
41851       [
41852         "Belarus (Беларусь)",
41853         "by",
41854         "375"
41855       ],
41856       [
41857         "Belgium (België)",
41858         "be",
41859         "32"
41860       ],
41861       [
41862         "Belize",
41863         "bz",
41864         "501"
41865       ],
41866       [
41867         "Benin (Bénin)",
41868         "bj",
41869         "229"
41870       ],
41871       [
41872         "Bermuda",
41873         "bm",
41874         "1441"
41875       ],
41876       [
41877         "Bhutan (འབྲུག)",
41878         "bt",
41879         "975"
41880       ],
41881       [
41882         "Bolivia",
41883         "bo",
41884         "591"
41885       ],
41886       [
41887         "Bosnia and Herzegovina (Босна и Херцеговина)",
41888         "ba",
41889         "387"
41890       ],
41891       [
41892         "Botswana",
41893         "bw",
41894         "267"
41895       ],
41896       [
41897         "Brazil (Brasil)",
41898         "br",
41899         "55"
41900       ],
41901       [
41902         "British Indian Ocean Territory",
41903         "io",
41904         "246"
41905       ],
41906       [
41907         "British Virgin Islands",
41908         "vg",
41909         "1284"
41910       ],
41911       [
41912         "Brunei",
41913         "bn",
41914         "673"
41915       ],
41916       [
41917         "Bulgaria (България)",
41918         "bg",
41919         "359"
41920       ],
41921       [
41922         "Burkina Faso",
41923         "bf",
41924         "226"
41925       ],
41926       [
41927         "Burundi (Uburundi)",
41928         "bi",
41929         "257"
41930       ],
41931       [
41932         "Cambodia (កម្ពុជា)",
41933         "kh",
41934         "855"
41935       ],
41936       [
41937         "Cameroon (Cameroun)",
41938         "cm",
41939         "237"
41940       ],
41941       [
41942         "Canada",
41943         "ca",
41944         "1",
41945         1,
41946         ["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"]
41947       ],
41948       [
41949         "Cape Verde (Kabu Verdi)",
41950         "cv",
41951         "238"
41952       ],
41953       [
41954         "Caribbean Netherlands",
41955         "bq",
41956         "599",
41957         1
41958       ],
41959       [
41960         "Cayman Islands",
41961         "ky",
41962         "1345"
41963       ],
41964       [
41965         "Central African Republic (République centrafricaine)",
41966         "cf",
41967         "236"
41968       ],
41969       [
41970         "Chad (Tchad)",
41971         "td",
41972         "235"
41973       ],
41974       [
41975         "Chile",
41976         "cl",
41977         "56"
41978       ],
41979       [
41980         "China (中国)",
41981         "cn",
41982         "86"
41983       ],
41984       [
41985         "Christmas Island",
41986         "cx",
41987         "61",
41988         2
41989       ],
41990       [
41991         "Cocos (Keeling) Islands",
41992         "cc",
41993         "61",
41994         1
41995       ],
41996       [
41997         "Colombia",
41998         "co",
41999         "57"
42000       ],
42001       [
42002         "Comoros (‫جزر القمر‬‎)",
42003         "km",
42004         "269"
42005       ],
42006       [
42007         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42008         "cd",
42009         "243"
42010       ],
42011       [
42012         "Congo (Republic) (Congo-Brazzaville)",
42013         "cg",
42014         "242"
42015       ],
42016       [
42017         "Cook Islands",
42018         "ck",
42019         "682"
42020       ],
42021       [
42022         "Costa Rica",
42023         "cr",
42024         "506"
42025       ],
42026       [
42027         "Côte d’Ivoire",
42028         "ci",
42029         "225"
42030       ],
42031       [
42032         "Croatia (Hrvatska)",
42033         "hr",
42034         "385"
42035       ],
42036       [
42037         "Cuba",
42038         "cu",
42039         "53"
42040       ],
42041       [
42042         "Curaçao",
42043         "cw",
42044         "599",
42045         0
42046       ],
42047       [
42048         "Cyprus (Κύπρος)",
42049         "cy",
42050         "357"
42051       ],
42052       [
42053         "Czech Republic (Česká republika)",
42054         "cz",
42055         "420"
42056       ],
42057       [
42058         "Denmark (Danmark)",
42059         "dk",
42060         "45"
42061       ],
42062       [
42063         "Djibouti",
42064         "dj",
42065         "253"
42066       ],
42067       [
42068         "Dominica",
42069         "dm",
42070         "1767"
42071       ],
42072       [
42073         "Dominican Republic (República Dominicana)",
42074         "do",
42075         "1",
42076         2,
42077         ["809", "829", "849"]
42078       ],
42079       [
42080         "Ecuador",
42081         "ec",
42082         "593"
42083       ],
42084       [
42085         "Egypt (‫مصر‬‎)",
42086         "eg",
42087         "20"
42088       ],
42089       [
42090         "El Salvador",
42091         "sv",
42092         "503"
42093       ],
42094       [
42095         "Equatorial Guinea (Guinea Ecuatorial)",
42096         "gq",
42097         "240"
42098       ],
42099       [
42100         "Eritrea",
42101         "er",
42102         "291"
42103       ],
42104       [
42105         "Estonia (Eesti)",
42106         "ee",
42107         "372"
42108       ],
42109       [
42110         "Ethiopia",
42111         "et",
42112         "251"
42113       ],
42114       [
42115         "Falkland Islands (Islas Malvinas)",
42116         "fk",
42117         "500"
42118       ],
42119       [
42120         "Faroe Islands (Føroyar)",
42121         "fo",
42122         "298"
42123       ],
42124       [
42125         "Fiji",
42126         "fj",
42127         "679"
42128       ],
42129       [
42130         "Finland (Suomi)",
42131         "fi",
42132         "358",
42133         0
42134       ],
42135       [
42136         "France",
42137         "fr",
42138         "33"
42139       ],
42140       [
42141         "French Guiana (Guyane française)",
42142         "gf",
42143         "594"
42144       ],
42145       [
42146         "French Polynesia (Polynésie française)",
42147         "pf",
42148         "689"
42149       ],
42150       [
42151         "Gabon",
42152         "ga",
42153         "241"
42154       ],
42155       [
42156         "Gambia",
42157         "gm",
42158         "220"
42159       ],
42160       [
42161         "Georgia (საქართველო)",
42162         "ge",
42163         "995"
42164       ],
42165       [
42166         "Germany (Deutschland)",
42167         "de",
42168         "49"
42169       ],
42170       [
42171         "Ghana (Gaana)",
42172         "gh",
42173         "233"
42174       ],
42175       [
42176         "Gibraltar",
42177         "gi",
42178         "350"
42179       ],
42180       [
42181         "Greece (Ελλάδα)",
42182         "gr",
42183         "30"
42184       ],
42185       [
42186         "Greenland (Kalaallit Nunaat)",
42187         "gl",
42188         "299"
42189       ],
42190       [
42191         "Grenada",
42192         "gd",
42193         "1473"
42194       ],
42195       [
42196         "Guadeloupe",
42197         "gp",
42198         "590",
42199         0
42200       ],
42201       [
42202         "Guam",
42203         "gu",
42204         "1671"
42205       ],
42206       [
42207         "Guatemala",
42208         "gt",
42209         "502"
42210       ],
42211       [
42212         "Guernsey",
42213         "gg",
42214         "44",
42215         1
42216       ],
42217       [
42218         "Guinea (Guinée)",
42219         "gn",
42220         "224"
42221       ],
42222       [
42223         "Guinea-Bissau (Guiné Bissau)",
42224         "gw",
42225         "245"
42226       ],
42227       [
42228         "Guyana",
42229         "gy",
42230         "592"
42231       ],
42232       [
42233         "Haiti",
42234         "ht",
42235         "509"
42236       ],
42237       [
42238         "Honduras",
42239         "hn",
42240         "504"
42241       ],
42242       [
42243         "Hong Kong (香港)",
42244         "hk",
42245         "852"
42246       ],
42247       [
42248         "Hungary (Magyarország)",
42249         "hu",
42250         "36"
42251       ],
42252       [
42253         "Iceland (Ísland)",
42254         "is",
42255         "354"
42256       ],
42257       [
42258         "India (भारत)",
42259         "in",
42260         "91"
42261       ],
42262       [
42263         "Indonesia",
42264         "id",
42265         "62"
42266       ],
42267       [
42268         "Iran (‫ایران‬‎)",
42269         "ir",
42270         "98"
42271       ],
42272       [
42273         "Iraq (‫العراق‬‎)",
42274         "iq",
42275         "964"
42276       ],
42277       [
42278         "Ireland",
42279         "ie",
42280         "353"
42281       ],
42282       [
42283         "Isle of Man",
42284         "im",
42285         "44",
42286         2
42287       ],
42288       [
42289         "Israel (‫ישראל‬‎)",
42290         "il",
42291         "972"
42292       ],
42293       [
42294         "Italy (Italia)",
42295         "it",
42296         "39",
42297         0
42298       ],
42299       [
42300         "Jamaica",
42301         "jm",
42302         "1876"
42303       ],
42304       [
42305         "Japan (日本)",
42306         "jp",
42307         "81"
42308       ],
42309       [
42310         "Jersey",
42311         "je",
42312         "44",
42313         3
42314       ],
42315       [
42316         "Jordan (‫الأردن‬‎)",
42317         "jo",
42318         "962"
42319       ],
42320       [
42321         "Kazakhstan (Казахстан)",
42322         "kz",
42323         "7",
42324         1
42325       ],
42326       [
42327         "Kenya",
42328         "ke",
42329         "254"
42330       ],
42331       [
42332         "Kiribati",
42333         "ki",
42334         "686"
42335       ],
42336       [
42337         "Kosovo",
42338         "xk",
42339         "383"
42340       ],
42341       [
42342         "Kuwait (‫الكويت‬‎)",
42343         "kw",
42344         "965"
42345       ],
42346       [
42347         "Kyrgyzstan (Кыргызстан)",
42348         "kg",
42349         "996"
42350       ],
42351       [
42352         "Laos (ລາວ)",
42353         "la",
42354         "856"
42355       ],
42356       [
42357         "Latvia (Latvija)",
42358         "lv",
42359         "371"
42360       ],
42361       [
42362         "Lebanon (‫لبنان‬‎)",
42363         "lb",
42364         "961"
42365       ],
42366       [
42367         "Lesotho",
42368         "ls",
42369         "266"
42370       ],
42371       [
42372         "Liberia",
42373         "lr",
42374         "231"
42375       ],
42376       [
42377         "Libya (‫ليبيا‬‎)",
42378         "ly",
42379         "218"
42380       ],
42381       [
42382         "Liechtenstein",
42383         "li",
42384         "423"
42385       ],
42386       [
42387         "Lithuania (Lietuva)",
42388         "lt",
42389         "370"
42390       ],
42391       [
42392         "Luxembourg",
42393         "lu",
42394         "352"
42395       ],
42396       [
42397         "Macau (澳門)",
42398         "mo",
42399         "853"
42400       ],
42401       [
42402         "Macedonia (FYROM) (Македонија)",
42403         "mk",
42404         "389"
42405       ],
42406       [
42407         "Madagascar (Madagasikara)",
42408         "mg",
42409         "261"
42410       ],
42411       [
42412         "Malawi",
42413         "mw",
42414         "265"
42415       ],
42416       [
42417         "Malaysia",
42418         "my",
42419         "60"
42420       ],
42421       [
42422         "Maldives",
42423         "mv",
42424         "960"
42425       ],
42426       [
42427         "Mali",
42428         "ml",
42429         "223"
42430       ],
42431       [
42432         "Malta",
42433         "mt",
42434         "356"
42435       ],
42436       [
42437         "Marshall Islands",
42438         "mh",
42439         "692"
42440       ],
42441       [
42442         "Martinique",
42443         "mq",
42444         "596"
42445       ],
42446       [
42447         "Mauritania (‫موريتانيا‬‎)",
42448         "mr",
42449         "222"
42450       ],
42451       [
42452         "Mauritius (Moris)",
42453         "mu",
42454         "230"
42455       ],
42456       [
42457         "Mayotte",
42458         "yt",
42459         "262",
42460         1
42461       ],
42462       [
42463         "Mexico (México)",
42464         "mx",
42465         "52"
42466       ],
42467       [
42468         "Micronesia",
42469         "fm",
42470         "691"
42471       ],
42472       [
42473         "Moldova (Republica Moldova)",
42474         "md",
42475         "373"
42476       ],
42477       [
42478         "Monaco",
42479         "mc",
42480         "377"
42481       ],
42482       [
42483         "Mongolia (Монгол)",
42484         "mn",
42485         "976"
42486       ],
42487       [
42488         "Montenegro (Crna Gora)",
42489         "me",
42490         "382"
42491       ],
42492       [
42493         "Montserrat",
42494         "ms",
42495         "1664"
42496       ],
42497       [
42498         "Morocco (‫المغرب‬‎)",
42499         "ma",
42500         "212",
42501         0
42502       ],
42503       [
42504         "Mozambique (Moçambique)",
42505         "mz",
42506         "258"
42507       ],
42508       [
42509         "Myanmar (Burma) (မြန်မာ)",
42510         "mm",
42511         "95"
42512       ],
42513       [
42514         "Namibia (Namibië)",
42515         "na",
42516         "264"
42517       ],
42518       [
42519         "Nauru",
42520         "nr",
42521         "674"
42522       ],
42523       [
42524         "Nepal (नेपाल)",
42525         "np",
42526         "977"
42527       ],
42528       [
42529         "Netherlands (Nederland)",
42530         "nl",
42531         "31"
42532       ],
42533       [
42534         "New Caledonia (Nouvelle-Calédonie)",
42535         "nc",
42536         "687"
42537       ],
42538       [
42539         "New Zealand",
42540         "nz",
42541         "64"
42542       ],
42543       [
42544         "Nicaragua",
42545         "ni",
42546         "505"
42547       ],
42548       [
42549         "Niger (Nijar)",
42550         "ne",
42551         "227"
42552       ],
42553       [
42554         "Nigeria",
42555         "ng",
42556         "234"
42557       ],
42558       [
42559         "Niue",
42560         "nu",
42561         "683"
42562       ],
42563       [
42564         "Norfolk Island",
42565         "nf",
42566         "672"
42567       ],
42568       [
42569         "North Korea (조선 민주주의 인민 공화국)",
42570         "kp",
42571         "850"
42572       ],
42573       [
42574         "Northern Mariana Islands",
42575         "mp",
42576         "1670"
42577       ],
42578       [
42579         "Norway (Norge)",
42580         "no",
42581         "47",
42582         0
42583       ],
42584       [
42585         "Oman (‫عُمان‬‎)",
42586         "om",
42587         "968"
42588       ],
42589       [
42590         "Pakistan (‫پاکستان‬‎)",
42591         "pk",
42592         "92"
42593       ],
42594       [
42595         "Palau",
42596         "pw",
42597         "680"
42598       ],
42599       [
42600         "Palestine (‫فلسطين‬‎)",
42601         "ps",
42602         "970"
42603       ],
42604       [
42605         "Panama (Panamá)",
42606         "pa",
42607         "507"
42608       ],
42609       [
42610         "Papua New Guinea",
42611         "pg",
42612         "675"
42613       ],
42614       [
42615         "Paraguay",
42616         "py",
42617         "595"
42618       ],
42619       [
42620         "Peru (Perú)",
42621         "pe",
42622         "51"
42623       ],
42624       [
42625         "Philippines",
42626         "ph",
42627         "63"
42628       ],
42629       [
42630         "Poland (Polska)",
42631         "pl",
42632         "48"
42633       ],
42634       [
42635         "Portugal",
42636         "pt",
42637         "351"
42638       ],
42639       [
42640         "Puerto Rico",
42641         "pr",
42642         "1",
42643         3,
42644         ["787", "939"]
42645       ],
42646       [
42647         "Qatar (‫قطر‬‎)",
42648         "qa",
42649         "974"
42650       ],
42651       [
42652         "Réunion (La Réunion)",
42653         "re",
42654         "262",
42655         0
42656       ],
42657       [
42658         "Romania (România)",
42659         "ro",
42660         "40"
42661       ],
42662       [
42663         "Russia (Россия)",
42664         "ru",
42665         "7",
42666         0
42667       ],
42668       [
42669         "Rwanda",
42670         "rw",
42671         "250"
42672       ],
42673       [
42674         "Saint Barthélemy",
42675         "bl",
42676         "590",
42677         1
42678       ],
42679       [
42680         "Saint Helena",
42681         "sh",
42682         "290"
42683       ],
42684       [
42685         "Saint Kitts and Nevis",
42686         "kn",
42687         "1869"
42688       ],
42689       [
42690         "Saint Lucia",
42691         "lc",
42692         "1758"
42693       ],
42694       [
42695         "Saint Martin (Saint-Martin (partie française))",
42696         "mf",
42697         "590",
42698         2
42699       ],
42700       [
42701         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42702         "pm",
42703         "508"
42704       ],
42705       [
42706         "Saint Vincent and the Grenadines",
42707         "vc",
42708         "1784"
42709       ],
42710       [
42711         "Samoa",
42712         "ws",
42713         "685"
42714       ],
42715       [
42716         "San Marino",
42717         "sm",
42718         "378"
42719       ],
42720       [
42721         "São Tomé and Príncipe (São Tomé e Príncipe)",
42722         "st",
42723         "239"
42724       ],
42725       [
42726         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42727         "sa",
42728         "966"
42729       ],
42730       [
42731         "Senegal (Sénégal)",
42732         "sn",
42733         "221"
42734       ],
42735       [
42736         "Serbia (Србија)",
42737         "rs",
42738         "381"
42739       ],
42740       [
42741         "Seychelles",
42742         "sc",
42743         "248"
42744       ],
42745       [
42746         "Sierra Leone",
42747         "sl",
42748         "232"
42749       ],
42750       [
42751         "Singapore",
42752         "sg",
42753         "65"
42754       ],
42755       [
42756         "Sint Maarten",
42757         "sx",
42758         "1721"
42759       ],
42760       [
42761         "Slovakia (Slovensko)",
42762         "sk",
42763         "421"
42764       ],
42765       [
42766         "Slovenia (Slovenija)",
42767         "si",
42768         "386"
42769       ],
42770       [
42771         "Solomon Islands",
42772         "sb",
42773         "677"
42774       ],
42775       [
42776         "Somalia (Soomaaliya)",
42777         "so",
42778         "252"
42779       ],
42780       [
42781         "South Africa",
42782         "za",
42783         "27"
42784       ],
42785       [
42786         "South Korea (대한민국)",
42787         "kr",
42788         "82"
42789       ],
42790       [
42791         "South Sudan (‫جنوب السودان‬‎)",
42792         "ss",
42793         "211"
42794       ],
42795       [
42796         "Spain (España)",
42797         "es",
42798         "34"
42799       ],
42800       [
42801         "Sri Lanka (ශ්‍රී ලංකාව)",
42802         "lk",
42803         "94"
42804       ],
42805       [
42806         "Sudan (‫السودان‬‎)",
42807         "sd",
42808         "249"
42809       ],
42810       [
42811         "Suriname",
42812         "sr",
42813         "597"
42814       ],
42815       [
42816         "Svalbard and Jan Mayen",
42817         "sj",
42818         "47",
42819         1
42820       ],
42821       [
42822         "Swaziland",
42823         "sz",
42824         "268"
42825       ],
42826       [
42827         "Sweden (Sverige)",
42828         "se",
42829         "46"
42830       ],
42831       [
42832         "Switzerland (Schweiz)",
42833         "ch",
42834         "41"
42835       ],
42836       [
42837         "Syria (‫سوريا‬‎)",
42838         "sy",
42839         "963"
42840       ],
42841       [
42842         "Taiwan (台灣)",
42843         "tw",
42844         "886"
42845       ],
42846       [
42847         "Tajikistan",
42848         "tj",
42849         "992"
42850       ],
42851       [
42852         "Tanzania",
42853         "tz",
42854         "255"
42855       ],
42856       [
42857         "Thailand (ไทย)",
42858         "th",
42859         "66"
42860       ],
42861       [
42862         "Timor-Leste",
42863         "tl",
42864         "670"
42865       ],
42866       [
42867         "Togo",
42868         "tg",
42869         "228"
42870       ],
42871       [
42872         "Tokelau",
42873         "tk",
42874         "690"
42875       ],
42876       [
42877         "Tonga",
42878         "to",
42879         "676"
42880       ],
42881       [
42882         "Trinidad and Tobago",
42883         "tt",
42884         "1868"
42885       ],
42886       [
42887         "Tunisia (‫تونس‬‎)",
42888         "tn",
42889         "216"
42890       ],
42891       [
42892         "Turkey (Türkiye)",
42893         "tr",
42894         "90"
42895       ],
42896       [
42897         "Turkmenistan",
42898         "tm",
42899         "993"
42900       ],
42901       [
42902         "Turks and Caicos Islands",
42903         "tc",
42904         "1649"
42905       ],
42906       [
42907         "Tuvalu",
42908         "tv",
42909         "688"
42910       ],
42911       [
42912         "U.S. Virgin Islands",
42913         "vi",
42914         "1340"
42915       ],
42916       [
42917         "Uganda",
42918         "ug",
42919         "256"
42920       ],
42921       [
42922         "Ukraine (Україна)",
42923         "ua",
42924         "380"
42925       ],
42926       [
42927         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42928         "ae",
42929         "971"
42930       ],
42931       [
42932         "United Kingdom",
42933         "gb",
42934         "44",
42935         0
42936       ],
42937       [
42938         "United States",
42939         "us",
42940         "1",
42941         0
42942       ],
42943       [
42944         "Uruguay",
42945         "uy",
42946         "598"
42947       ],
42948       [
42949         "Uzbekistan (Oʻzbekiston)",
42950         "uz",
42951         "998"
42952       ],
42953       [
42954         "Vanuatu",
42955         "vu",
42956         "678"
42957       ],
42958       [
42959         "Vatican City (Città del Vaticano)",
42960         "va",
42961         "39",
42962         1
42963       ],
42964       [
42965         "Venezuela",
42966         "ve",
42967         "58"
42968       ],
42969       [
42970         "Vietnam (Việt Nam)",
42971         "vn",
42972         "84"
42973       ],
42974       [
42975         "Wallis and Futuna (Wallis-et-Futuna)",
42976         "wf",
42977         "681"
42978       ],
42979       [
42980         "Western Sahara (‫الصحراء الغربية‬‎)",
42981         "eh",
42982         "212",
42983         1
42984       ],
42985       [
42986         "Yemen (‫اليمن‬‎)",
42987         "ye",
42988         "967"
42989       ],
42990       [
42991         "Zambia",
42992         "zm",
42993         "260"
42994       ],
42995       [
42996         "Zimbabwe",
42997         "zw",
42998         "263"
42999       ],
43000       [
43001         "Åland Islands",
43002         "ax",
43003         "358",
43004         1
43005       ]
43006   ];
43007   
43008   return d;
43009 }/**
43010 *    This script refer to:
43011 *    Title: International Telephone Input
43012 *    Author: Jack O'Connor
43013 *    Code version:  v12.1.12
43014 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43015 **/
43016
43017 /**
43018  * @class Roo.bootstrap.PhoneInput
43019  * @extends Roo.bootstrap.TriggerField
43020  * An input with International dial-code selection
43021  
43022  * @cfg {String} defaultDialCode default '+852'
43023  * @cfg {Array} preferedCountries default []
43024   
43025  * @constructor
43026  * Create a new PhoneInput.
43027  * @param {Object} config Configuration options
43028  */
43029
43030 Roo.bootstrap.PhoneInput = function(config) {
43031     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43032 };
43033
43034 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43035         
43036         listWidth: undefined,
43037         
43038         selectedClass: 'active',
43039         
43040         invalidClass : "has-warning",
43041         
43042         validClass: 'has-success',
43043         
43044         allowed: '0123456789',
43045         
43046         max_length: 15,
43047         
43048         /**
43049          * @cfg {String} defaultDialCode The default dial code when initializing the input
43050          */
43051         defaultDialCode: '+852',
43052         
43053         /**
43054          * @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
43055          */
43056         preferedCountries: false,
43057         
43058         getAutoCreate : function()
43059         {
43060             var data = Roo.bootstrap.PhoneInputData();
43061             var align = this.labelAlign || this.parentLabelAlign();
43062             var id = Roo.id();
43063             
43064             this.allCountries = [];
43065             this.dialCodeMapping = [];
43066             
43067             for (var i = 0; i < data.length; i++) {
43068               var c = data[i];
43069               this.allCountries[i] = {
43070                 name: c[0],
43071                 iso2: c[1],
43072                 dialCode: c[2],
43073                 priority: c[3] || 0,
43074                 areaCodes: c[4] || null
43075               };
43076               this.dialCodeMapping[c[2]] = {
43077                   name: c[0],
43078                   iso2: c[1],
43079                   priority: c[3] || 0,
43080                   areaCodes: c[4] || null
43081               };
43082             }
43083             
43084             var cfg = {
43085                 cls: 'form-group',
43086                 cn: []
43087             };
43088             
43089             var input =  {
43090                 tag: 'input',
43091                 id : id,
43092                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43093                 maxlength: this.max_length,
43094                 cls : 'form-control tel-input',
43095                 autocomplete: 'new-password'
43096             };
43097             
43098             var hiddenInput = {
43099                 tag: 'input',
43100                 type: 'hidden',
43101                 cls: 'hidden-tel-input'
43102             };
43103             
43104             if (this.name) {
43105                 hiddenInput.name = this.name;
43106             }
43107             
43108             if (this.disabled) {
43109                 input.disabled = true;
43110             }
43111             
43112             var flag_container = {
43113                 tag: 'div',
43114                 cls: 'flag-box',
43115                 cn: [
43116                     {
43117                         tag: 'div',
43118                         cls: 'flag'
43119                     },
43120                     {
43121                         tag: 'div',
43122                         cls: 'caret'
43123                     }
43124                 ]
43125             };
43126             
43127             var box = {
43128                 tag: 'div',
43129                 cls: this.hasFeedback ? 'has-feedback' : '',
43130                 cn: [
43131                     hiddenInput,
43132                     input,
43133                     {
43134                         tag: 'input',
43135                         cls: 'dial-code-holder',
43136                         disabled: true
43137                     }
43138                 ]
43139             };
43140             
43141             var container = {
43142                 cls: 'roo-select2-container input-group',
43143                 cn: [
43144                     flag_container,
43145                     box
43146                 ]
43147             };
43148             
43149             if (this.fieldLabel.length) {
43150                 var indicator = {
43151                     tag: 'i',
43152                     tooltip: 'This field is required'
43153                 };
43154                 
43155                 var label = {
43156                     tag: 'label',
43157                     'for':  id,
43158                     cls: 'control-label',
43159                     cn: []
43160                 };
43161                 
43162                 var label_text = {
43163                     tag: 'span',
43164                     html: this.fieldLabel
43165                 };
43166                 
43167                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43168                 label.cn = [
43169                     indicator,
43170                     label_text
43171                 ];
43172                 
43173                 if(this.indicatorpos == 'right') {
43174                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43175                     label.cn = [
43176                         label_text,
43177                         indicator
43178                     ];
43179                 }
43180                 
43181                 if(align == 'left') {
43182                     container = {
43183                         tag: 'div',
43184                         cn: [
43185                             container
43186                         ]
43187                     };
43188                     
43189                     if(this.labelWidth > 12){
43190                         label.style = "width: " + this.labelWidth + 'px';
43191                     }
43192                     if(this.labelWidth < 13 && this.labelmd == 0){
43193                         this.labelmd = this.labelWidth;
43194                     }
43195                     if(this.labellg > 0){
43196                         label.cls += ' col-lg-' + this.labellg;
43197                         input.cls += ' col-lg-' + (12 - this.labellg);
43198                     }
43199                     if(this.labelmd > 0){
43200                         label.cls += ' col-md-' + this.labelmd;
43201                         container.cls += ' col-md-' + (12 - this.labelmd);
43202                     }
43203                     if(this.labelsm > 0){
43204                         label.cls += ' col-sm-' + this.labelsm;
43205                         container.cls += ' col-sm-' + (12 - this.labelsm);
43206                     }
43207                     if(this.labelxs > 0){
43208                         label.cls += ' col-xs-' + this.labelxs;
43209                         container.cls += ' col-xs-' + (12 - this.labelxs);
43210                     }
43211                 }
43212             }
43213             
43214             cfg.cn = [
43215                 label,
43216                 container
43217             ];
43218             
43219             var settings = this;
43220             
43221             ['xs','sm','md','lg'].map(function(size){
43222                 if (settings[size]) {
43223                     cfg.cls += ' col-' + size + '-' + settings[size];
43224                 }
43225             });
43226             
43227             this.store = new Roo.data.Store({
43228                 proxy : new Roo.data.MemoryProxy({}),
43229                 reader : new Roo.data.JsonReader({
43230                     fields : [
43231                         {
43232                             'name' : 'name',
43233                             'type' : 'string'
43234                         },
43235                         {
43236                             'name' : 'iso2',
43237                             'type' : 'string'
43238                         },
43239                         {
43240                             'name' : 'dialCode',
43241                             'type' : 'string'
43242                         },
43243                         {
43244                             'name' : 'priority',
43245                             'type' : 'string'
43246                         },
43247                         {
43248                             'name' : 'areaCodes',
43249                             'type' : 'string'
43250                         }
43251                     ]
43252                 })
43253             });
43254             
43255             if(!this.preferedCountries) {
43256                 this.preferedCountries = [
43257                     'hk',
43258                     'gb',
43259                     'us'
43260                 ];
43261             }
43262             
43263             var p = this.preferedCountries.reverse();
43264             
43265             if(p) {
43266                 for (var i = 0; i < p.length; i++) {
43267                     for (var j = 0; j < this.allCountries.length; j++) {
43268                         if(this.allCountries[j].iso2 == p[i]) {
43269                             var t = this.allCountries[j];
43270                             this.allCountries.splice(j,1);
43271                             this.allCountries.unshift(t);
43272                         }
43273                     } 
43274                 }
43275             }
43276             
43277             this.store.proxy.data = {
43278                 success: true,
43279                 data: this.allCountries
43280             };
43281             
43282             return cfg;
43283         },
43284         
43285         initEvents : function()
43286         {
43287             this.createList();
43288             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43289             
43290             this.indicator = this.indicatorEl();
43291             this.flag = this.flagEl();
43292             this.dialCodeHolder = this.dialCodeHolderEl();
43293             
43294             this.trigger = this.el.select('div.flag-box',true).first();
43295             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43296             
43297             var _this = this;
43298             
43299             (function(){
43300                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43301                 _this.list.setWidth(lw);
43302             }).defer(100);
43303             
43304             this.list.on('mouseover', this.onViewOver, this);
43305             this.list.on('mousemove', this.onViewMove, this);
43306             this.inputEl().on("keyup", this.onKeyUp, this);
43307             this.inputEl().on("keypress", this.onKeyPress, this);
43308             
43309             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43310
43311             this.view = new Roo.View(this.list, this.tpl, {
43312                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43313             });
43314             
43315             this.view.on('click', this.onViewClick, this);
43316             this.setValue(this.defaultDialCode);
43317         },
43318         
43319         onTriggerClick : function(e)
43320         {
43321             Roo.log('trigger click');
43322             if(this.disabled){
43323                 return;
43324             }
43325             
43326             if(this.isExpanded()){
43327                 this.collapse();
43328                 this.hasFocus = false;
43329             }else {
43330                 this.store.load({});
43331                 this.hasFocus = true;
43332                 this.expand();
43333             }
43334         },
43335         
43336         isExpanded : function()
43337         {
43338             return this.list.isVisible();
43339         },
43340         
43341         collapse : function()
43342         {
43343             if(!this.isExpanded()){
43344                 return;
43345             }
43346             this.list.hide();
43347             Roo.get(document).un('mousedown', this.collapseIf, this);
43348             Roo.get(document).un('mousewheel', this.collapseIf, this);
43349             this.fireEvent('collapse', this);
43350             this.validate();
43351         },
43352         
43353         expand : function()
43354         {
43355             Roo.log('expand');
43356
43357             if(this.isExpanded() || !this.hasFocus){
43358                 return;
43359             }
43360             
43361             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43362             this.list.setWidth(lw);
43363             
43364             this.list.show();
43365             this.restrictHeight();
43366             
43367             Roo.get(document).on('mousedown', this.collapseIf, this);
43368             Roo.get(document).on('mousewheel', this.collapseIf, this);
43369             
43370             this.fireEvent('expand', this);
43371         },
43372         
43373         restrictHeight : function()
43374         {
43375             this.list.alignTo(this.inputEl(), this.listAlign);
43376             this.list.alignTo(this.inputEl(), this.listAlign);
43377         },
43378         
43379         onViewOver : function(e, t)
43380         {
43381             if(this.inKeyMode){
43382                 return;
43383             }
43384             var item = this.view.findItemFromChild(t);
43385             
43386             if(item){
43387                 var index = this.view.indexOf(item);
43388                 this.select(index, false);
43389             }
43390         },
43391
43392         // private
43393         onViewClick : function(view, doFocus, el, e)
43394         {
43395             var index = this.view.getSelectedIndexes()[0];
43396             
43397             var r = this.store.getAt(index);
43398             
43399             if(r){
43400                 this.onSelect(r, index);
43401             }
43402             if(doFocus !== false && !this.blockFocus){
43403                 this.inputEl().focus();
43404             }
43405         },
43406         
43407         onViewMove : function(e, t)
43408         {
43409             this.inKeyMode = false;
43410         },
43411         
43412         select : function(index, scrollIntoView)
43413         {
43414             this.selectedIndex = index;
43415             this.view.select(index);
43416             if(scrollIntoView !== false){
43417                 var el = this.view.getNode(index);
43418                 if(el){
43419                     this.list.scrollChildIntoView(el, false);
43420                 }
43421             }
43422         },
43423         
43424         createList : function()
43425         {
43426             this.list = Roo.get(document.body).createChild({
43427                 tag: 'ul',
43428                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43429                 style: 'display:none'
43430             });
43431             
43432             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43433         },
43434         
43435         collapseIf : function(e)
43436         {
43437             var in_combo  = e.within(this.el);
43438             var in_list =  e.within(this.list);
43439             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43440             
43441             if (in_combo || in_list || is_list) {
43442                 return;
43443             }
43444             this.collapse();
43445         },
43446         
43447         onSelect : function(record, index)
43448         {
43449             if(this.fireEvent('beforeselect', this, record, index) !== false){
43450                 
43451                 this.setFlagClass(record.data.iso2);
43452                 this.setDialCode(record.data.dialCode);
43453                 this.hasFocus = false;
43454                 this.collapse();
43455                 this.fireEvent('select', this, record, index);
43456             }
43457         },
43458         
43459         flagEl : function()
43460         {
43461             var flag = this.el.select('div.flag',true).first();
43462             if(!flag){
43463                 return false;
43464             }
43465             return flag;
43466         },
43467         
43468         dialCodeHolderEl : function()
43469         {
43470             var d = this.el.select('input.dial-code-holder',true).first();
43471             if(!d){
43472                 return false;
43473             }
43474             return d;
43475         },
43476         
43477         setDialCode : function(v)
43478         {
43479             this.dialCodeHolder.dom.value = '+'+v;
43480         },
43481         
43482         setFlagClass : function(n)
43483         {
43484             this.flag.dom.className = 'flag '+n;
43485         },
43486         
43487         getValue : function()
43488         {
43489             var v = this.inputEl().getValue();
43490             if(this.dialCodeHolder) {
43491                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43492             }
43493             return v;
43494         },
43495         
43496         setValue : function(v)
43497         {
43498             var d = this.getDialCode(v);
43499             
43500             //invalid dial code
43501             if(v.length == 0 || !d || d.length == 0) {
43502                 if(this.rendered){
43503                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43504                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43505                 }
43506                 return;
43507             }
43508             
43509             //valid dial code
43510             this.setFlagClass(this.dialCodeMapping[d].iso2);
43511             this.setDialCode(d);
43512             this.inputEl().dom.value = v.replace('+'+d,'');
43513             this.hiddenEl().dom.value = this.getValue();
43514             
43515             this.validate();
43516         },
43517         
43518         getDialCode : function(v)
43519         {
43520             v = v ||  '';
43521             
43522             if (v.length == 0) {
43523                 return this.dialCodeHolder.dom.value;
43524             }
43525             
43526             var dialCode = "";
43527             if (v.charAt(0) != "+") {
43528                 return false;
43529             }
43530             var numericChars = "";
43531             for (var i = 1; i < v.length; i++) {
43532               var c = v.charAt(i);
43533               if (!isNaN(c)) {
43534                 numericChars += c;
43535                 if (this.dialCodeMapping[numericChars]) {
43536                   dialCode = v.substr(1, i);
43537                 }
43538                 if (numericChars.length == 4) {
43539                   break;
43540                 }
43541               }
43542             }
43543             return dialCode;
43544         },
43545         
43546         reset : function()
43547         {
43548             this.setValue(this.defaultDialCode);
43549             this.validate();
43550         },
43551         
43552         hiddenEl : function()
43553         {
43554             return this.el.select('input.hidden-tel-input',true).first();
43555         },
43556         
43557         // after setting val
43558         onKeyUp : function(e){
43559             this.setValue(this.getValue());
43560         },
43561         
43562         onKeyPress : function(e){
43563             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43564                 e.stopEvent();
43565             }
43566         }
43567         
43568 });
43569 /**
43570  * @class Roo.bootstrap.MoneyField
43571  * @extends Roo.bootstrap.ComboBox
43572  * Bootstrap MoneyField class
43573  * 
43574  * @constructor
43575  * Create a new MoneyField.
43576  * @param {Object} config Configuration options
43577  */
43578
43579 Roo.bootstrap.MoneyField = function(config) {
43580     
43581     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43582     
43583 };
43584
43585 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43586     
43587     /**
43588      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43589      */
43590     allowDecimals : true,
43591     /**
43592      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43593      */
43594     decimalSeparator : ".",
43595     /**
43596      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43597      */
43598     decimalPrecision : 0,
43599     /**
43600      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43601      */
43602     allowNegative : true,
43603     /**
43604      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43605      */
43606     allowZero: true,
43607     /**
43608      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43609      */
43610     minValue : Number.NEGATIVE_INFINITY,
43611     /**
43612      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43613      */
43614     maxValue : Number.MAX_VALUE,
43615     /**
43616      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43617      */
43618     minText : "The minimum value for this field is {0}",
43619     /**
43620      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43621      */
43622     maxText : "The maximum value for this field is {0}",
43623     /**
43624      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43625      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43626      */
43627     nanText : "{0} is not a valid number",
43628     /**
43629      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43630      */
43631     castInt : true,
43632     /**
43633      * @cfg {String} defaults currency of the MoneyField
43634      * value should be in lkey
43635      */
43636     defaultCurrency : false,
43637     /**
43638      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43639      */
43640     thousandsDelimiter : false,
43641     /**
43642      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43643      */
43644     max_length: false,
43645     
43646     inputlg : 9,
43647     inputmd : 9,
43648     inputsm : 9,
43649     inputxs : 6,
43650     
43651     store : false,
43652     
43653     getAutoCreate : function()
43654     {
43655         var align = this.labelAlign || this.parentLabelAlign();
43656         
43657         var id = Roo.id();
43658
43659         var cfg = {
43660             cls: 'form-group',
43661             cn: []
43662         };
43663
43664         var input =  {
43665             tag: 'input',
43666             id : id,
43667             cls : 'form-control roo-money-amount-input',
43668             autocomplete: 'new-password'
43669         };
43670         
43671         var hiddenInput = {
43672             tag: 'input',
43673             type: 'hidden',
43674             id: Roo.id(),
43675             cls: 'hidden-number-input'
43676         };
43677         
43678         if(this.max_length) {
43679             input.maxlength = this.max_length; 
43680         }
43681         
43682         if (this.name) {
43683             hiddenInput.name = this.name;
43684         }
43685
43686         if (this.disabled) {
43687             input.disabled = true;
43688         }
43689
43690         var clg = 12 - this.inputlg;
43691         var cmd = 12 - this.inputmd;
43692         var csm = 12 - this.inputsm;
43693         var cxs = 12 - this.inputxs;
43694         
43695         var container = {
43696             tag : 'div',
43697             cls : 'row roo-money-field',
43698             cn : [
43699                 {
43700                     tag : 'div',
43701                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43702                     cn : [
43703                         {
43704                             tag : 'div',
43705                             cls: 'roo-select2-container input-group',
43706                             cn: [
43707                                 {
43708                                     tag : 'input',
43709                                     cls : 'form-control roo-money-currency-input',
43710                                     autocomplete: 'new-password',
43711                                     readOnly : 1,
43712                                     name : this.currencyName
43713                                 },
43714                                 {
43715                                     tag :'span',
43716                                     cls : 'input-group-addon',
43717                                     cn : [
43718                                         {
43719                                             tag: 'span',
43720                                             cls: 'caret'
43721                                         }
43722                                     ]
43723                                 }
43724                             ]
43725                         }
43726                     ]
43727                 },
43728                 {
43729                     tag : 'div',
43730                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43731                     cn : [
43732                         {
43733                             tag: 'div',
43734                             cls: this.hasFeedback ? 'has-feedback' : '',
43735                             cn: [
43736                                 input
43737                             ]
43738                         }
43739                     ]
43740                 }
43741             ]
43742             
43743         };
43744         
43745         if (this.fieldLabel.length) {
43746             var indicator = {
43747                 tag: 'i',
43748                 tooltip: 'This field is required'
43749             };
43750
43751             var label = {
43752                 tag: 'label',
43753                 'for':  id,
43754                 cls: 'control-label',
43755                 cn: []
43756             };
43757
43758             var label_text = {
43759                 tag: 'span',
43760                 html: this.fieldLabel
43761             };
43762
43763             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43764             label.cn = [
43765                 indicator,
43766                 label_text
43767             ];
43768
43769             if(this.indicatorpos == 'right') {
43770                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43771                 label.cn = [
43772                     label_text,
43773                     indicator
43774                 ];
43775             }
43776
43777             if(align == 'left') {
43778                 container = {
43779                     tag: 'div',
43780                     cn: [
43781                         container
43782                     ]
43783                 };
43784
43785                 if(this.labelWidth > 12){
43786                     label.style = "width: " + this.labelWidth + 'px';
43787                 }
43788                 if(this.labelWidth < 13 && this.labelmd == 0){
43789                     this.labelmd = this.labelWidth;
43790                 }
43791                 if(this.labellg > 0){
43792                     label.cls += ' col-lg-' + this.labellg;
43793                     input.cls += ' col-lg-' + (12 - this.labellg);
43794                 }
43795                 if(this.labelmd > 0){
43796                     label.cls += ' col-md-' + this.labelmd;
43797                     container.cls += ' col-md-' + (12 - this.labelmd);
43798                 }
43799                 if(this.labelsm > 0){
43800                     label.cls += ' col-sm-' + this.labelsm;
43801                     container.cls += ' col-sm-' + (12 - this.labelsm);
43802                 }
43803                 if(this.labelxs > 0){
43804                     label.cls += ' col-xs-' + this.labelxs;
43805                     container.cls += ' col-xs-' + (12 - this.labelxs);
43806                 }
43807             }
43808         }
43809
43810         cfg.cn = [
43811             label,
43812             container,
43813             hiddenInput
43814         ];
43815         
43816         var settings = this;
43817
43818         ['xs','sm','md','lg'].map(function(size){
43819             if (settings[size]) {
43820                 cfg.cls += ' col-' + size + '-' + settings[size];
43821             }
43822         });
43823         
43824         return cfg;
43825     },
43826     
43827     initEvents : function()
43828     {
43829         this.indicator = this.indicatorEl();
43830         
43831         this.initCurrencyEvent();
43832         
43833         this.initNumberEvent();
43834     },
43835     
43836     initCurrencyEvent : function()
43837     {
43838         if (!this.store) {
43839             throw "can not find store for combo";
43840         }
43841         
43842         this.store = Roo.factory(this.store, Roo.data);
43843         this.store.parent = this;
43844         
43845         this.createList();
43846         
43847         this.triggerEl = this.el.select('.input-group-addon', true).first();
43848         
43849         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43850         
43851         var _this = this;
43852         
43853         (function(){
43854             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43855             _this.list.setWidth(lw);
43856         }).defer(100);
43857         
43858         this.list.on('mouseover', this.onViewOver, this);
43859         this.list.on('mousemove', this.onViewMove, this);
43860         this.list.on('scroll', this.onViewScroll, this);
43861         
43862         if(!this.tpl){
43863             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43864         }
43865         
43866         this.view = new Roo.View(this.list, this.tpl, {
43867             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43868         });
43869         
43870         this.view.on('click', this.onViewClick, this);
43871         
43872         this.store.on('beforeload', this.onBeforeLoad, this);
43873         this.store.on('load', this.onLoad, this);
43874         this.store.on('loadexception', this.onLoadException, this);
43875         
43876         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43877             "up" : function(e){
43878                 this.inKeyMode = true;
43879                 this.selectPrev();
43880             },
43881
43882             "down" : function(e){
43883                 if(!this.isExpanded()){
43884                     this.onTriggerClick();
43885                 }else{
43886                     this.inKeyMode = true;
43887                     this.selectNext();
43888                 }
43889             },
43890
43891             "enter" : function(e){
43892                 this.collapse();
43893                 
43894                 if(this.fireEvent("specialkey", this, e)){
43895                     this.onViewClick(false);
43896                 }
43897                 
43898                 return true;
43899             },
43900
43901             "esc" : function(e){
43902                 this.collapse();
43903             },
43904
43905             "tab" : function(e){
43906                 this.collapse();
43907                 
43908                 if(this.fireEvent("specialkey", this, e)){
43909                     this.onViewClick(false);
43910                 }
43911                 
43912                 return true;
43913             },
43914
43915             scope : this,
43916
43917             doRelay : function(foo, bar, hname){
43918                 if(hname == 'down' || this.scope.isExpanded()){
43919                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43920                 }
43921                 return true;
43922             },
43923
43924             forceKeyDown: true
43925         });
43926         
43927         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43928         
43929     },
43930     
43931     initNumberEvent : function(e)
43932     {
43933         this.inputEl().on("keydown" , this.fireKey,  this);
43934         this.inputEl().on("focus", this.onFocus,  this);
43935         this.inputEl().on("blur", this.onBlur,  this);
43936         
43937         this.inputEl().relayEvent('keyup', this);
43938         
43939         if(this.indicator){
43940             this.indicator.addClass('invisible');
43941         }
43942  
43943         this.originalValue = this.getValue();
43944         
43945         if(this.validationEvent == 'keyup'){
43946             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43947             this.inputEl().on('keyup', this.filterValidation, this);
43948         }
43949         else if(this.validationEvent !== false){
43950             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43951         }
43952         
43953         if(this.selectOnFocus){
43954             this.on("focus", this.preFocus, this);
43955             
43956         }
43957         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43958             this.inputEl().on("keypress", this.filterKeys, this);
43959         } else {
43960             this.inputEl().relayEvent('keypress', this);
43961         }
43962         
43963         var allowed = "0123456789";
43964         
43965         if(this.allowDecimals){
43966             allowed += this.decimalSeparator;
43967         }
43968         
43969         if(this.allowNegative){
43970             allowed += "-";
43971         }
43972         
43973         if(this.thousandsDelimiter) {
43974             allowed += ",";
43975         }
43976         
43977         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43978         
43979         var keyPress = function(e){
43980             
43981             var k = e.getKey();
43982             
43983             var c = e.getCharCode();
43984             
43985             if(
43986                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43987                     allowed.indexOf(String.fromCharCode(c)) === -1
43988             ){
43989                 e.stopEvent();
43990                 return;
43991             }
43992             
43993             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43994                 return;
43995             }
43996             
43997             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43998                 e.stopEvent();
43999             }
44000         };
44001         
44002         this.inputEl().on("keypress", keyPress, this);
44003         
44004     },
44005     
44006     onTriggerClick : function(e)
44007     {   
44008         if(this.disabled){
44009             return;
44010         }
44011         
44012         this.page = 0;
44013         this.loadNext = false;
44014         
44015         if(this.isExpanded()){
44016             this.collapse();
44017             return;
44018         }
44019         
44020         this.hasFocus = true;
44021         
44022         if(this.triggerAction == 'all') {
44023             this.doQuery(this.allQuery, true);
44024             return;
44025         }
44026         
44027         this.doQuery(this.getRawValue());
44028     },
44029     
44030     getCurrency : function()
44031     {   
44032         var v = this.currencyEl().getValue();
44033         
44034         return v;
44035     },
44036     
44037     restrictHeight : function()
44038     {
44039         this.list.alignTo(this.currencyEl(), this.listAlign);
44040         this.list.alignTo(this.currencyEl(), this.listAlign);
44041     },
44042     
44043     onViewClick : function(view, doFocus, el, e)
44044     {
44045         var index = this.view.getSelectedIndexes()[0];
44046         
44047         var r = this.store.getAt(index);
44048         
44049         if(r){
44050             this.onSelect(r, index);
44051         }
44052     },
44053     
44054     onSelect : function(record, index){
44055         
44056         if(this.fireEvent('beforeselect', this, record, index) !== false){
44057         
44058             this.setFromCurrencyData(index > -1 ? record.data : false);
44059             
44060             this.collapse();
44061             
44062             this.fireEvent('select', this, record, index);
44063         }
44064     },
44065     
44066     setFromCurrencyData : function(o)
44067     {
44068         var currency = '';
44069         
44070         this.lastCurrency = o;
44071         
44072         if (this.currencyField) {
44073             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44074         } else {
44075             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44076         }
44077         
44078         this.lastSelectionText = currency;
44079         
44080         //setting default currency
44081         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44082             this.setCurrency(this.defaultCurrency);
44083             return;
44084         }
44085         
44086         this.setCurrency(currency);
44087     },
44088     
44089     setFromData : function(o)
44090     {
44091         var c = {};
44092         
44093         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44094         
44095         this.setFromCurrencyData(c);
44096         
44097         var value = '';
44098         
44099         if (this.name) {
44100             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44101         } else {
44102             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44103         }
44104         
44105         this.setValue(value);
44106         
44107     },
44108     
44109     setCurrency : function(v)
44110     {   
44111         this.currencyValue = v;
44112         
44113         if(this.rendered){
44114             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44115             this.validate();
44116         }
44117     },
44118     
44119     setValue : function(v)
44120     {
44121         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44122         
44123         this.value = v;
44124         
44125         if(this.rendered){
44126             
44127             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44128             
44129             this.inputEl().dom.value = (v == '') ? '' :
44130                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44131             
44132             if(!this.allowZero && v === '0') {
44133                 this.hiddenEl().dom.value = '';
44134                 this.inputEl().dom.value = '';
44135             }
44136             
44137             this.validate();
44138         }
44139     },
44140     
44141     getRawValue : function()
44142     {
44143         var v = this.inputEl().getValue();
44144         
44145         return v;
44146     },
44147     
44148     getValue : function()
44149     {
44150         return this.fixPrecision(this.parseValue(this.getRawValue()));
44151     },
44152     
44153     parseValue : function(value)
44154     {
44155         if(this.thousandsDelimiter) {
44156             value += "";
44157             r = new RegExp(",", "g");
44158             value = value.replace(r, "");
44159         }
44160         
44161         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44162         return isNaN(value) ? '' : value;
44163         
44164     },
44165     
44166     fixPrecision : function(value)
44167     {
44168         if(this.thousandsDelimiter) {
44169             value += "";
44170             r = new RegExp(",", "g");
44171             value = value.replace(r, "");
44172         }
44173         
44174         var nan = isNaN(value);
44175         
44176         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44177             return nan ? '' : value;
44178         }
44179         return parseFloat(value).toFixed(this.decimalPrecision);
44180     },
44181     
44182     decimalPrecisionFcn : function(v)
44183     {
44184         return Math.floor(v);
44185     },
44186     
44187     validateValue : function(value)
44188     {
44189         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44190             return false;
44191         }
44192         
44193         var num = this.parseValue(value);
44194         
44195         if(isNaN(num)){
44196             this.markInvalid(String.format(this.nanText, value));
44197             return false;
44198         }
44199         
44200         if(num < this.minValue){
44201             this.markInvalid(String.format(this.minText, this.minValue));
44202             return false;
44203         }
44204         
44205         if(num > this.maxValue){
44206             this.markInvalid(String.format(this.maxText, this.maxValue));
44207             return false;
44208         }
44209         
44210         return true;
44211     },
44212     
44213     validate : function()
44214     {
44215         if(this.disabled || this.allowBlank){
44216             this.markValid();
44217             return true;
44218         }
44219         
44220         var currency = this.getCurrency();
44221         
44222         if(this.validateValue(this.getRawValue()) && currency.length){
44223             this.markValid();
44224             return true;
44225         }
44226         
44227         this.markInvalid();
44228         return false;
44229     },
44230     
44231     getName: function()
44232     {
44233         return this.name;
44234     },
44235     
44236     beforeBlur : function()
44237     {
44238         if(!this.castInt){
44239             return;
44240         }
44241         
44242         var v = this.parseValue(this.getRawValue());
44243         
44244         if(v || v == 0){
44245             this.setValue(v);
44246         }
44247     },
44248     
44249     onBlur : function()
44250     {
44251         this.beforeBlur();
44252         
44253         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44254             //this.el.removeClass(this.focusClass);
44255         }
44256         
44257         this.hasFocus = false;
44258         
44259         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44260             this.validate();
44261         }
44262         
44263         var v = this.getValue();
44264         
44265         if(String(v) !== String(this.startValue)){
44266             this.fireEvent('change', this, v, this.startValue);
44267         }
44268         
44269         this.fireEvent("blur", this);
44270     },
44271     
44272     inputEl : function()
44273     {
44274         return this.el.select('.roo-money-amount-input', true).first();
44275     },
44276     
44277     currencyEl : function()
44278     {
44279         return this.el.select('.roo-money-currency-input', true).first();
44280     },
44281     
44282     hiddenEl : function()
44283     {
44284         return this.el.select('input.hidden-number-input',true).first();
44285     }
44286     
44287 });/**
44288  * @class Roo.bootstrap.BezierSignature
44289  * @extends Roo.bootstrap.Component
44290  * Bootstrap BezierSignature class
44291  * This script refer to:
44292  *    Title: Signature Pad
44293  *    Author: szimek
44294  *    Availability: https://github.com/szimek/signature_pad
44295  *
44296  * @constructor
44297  * Create a new BezierSignature
44298  * @param {Object} config The config object
44299  */
44300
44301 Roo.bootstrap.BezierSignature = function(config){
44302     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44303     this.addEvents({
44304         "resize" : true
44305     });
44306 };
44307
44308 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44309 {
44310      
44311     curve_data: [],
44312     
44313     is_empty: true,
44314     
44315     mouse_btn_down: true,
44316     
44317     /**
44318      * @cfg {int} canvas height
44319      */
44320     canvas_height: '200px',
44321     
44322     /**
44323      * @cfg {float|function} Radius of a single dot.
44324      */ 
44325     dot_size: false,
44326     
44327     /**
44328      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44329      */
44330     min_width: 0.5,
44331     
44332     /**
44333      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44334      */
44335     max_width: 2.5,
44336     
44337     /**
44338      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44339      */
44340     throttle: 16,
44341     
44342     /**
44343      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44344      */
44345     min_distance: 5,
44346     
44347     /**
44348      * @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.
44349      */
44350     bg_color: 'rgba(0, 0, 0, 0)',
44351     
44352     /**
44353      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44354      */
44355     dot_color: 'black',
44356     
44357     /**
44358      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44359      */ 
44360     velocity_filter_weight: 0.7,
44361     
44362     /**
44363      * @cfg {function} Callback when stroke begin. 
44364      */
44365     onBegin: false,
44366     
44367     /**
44368      * @cfg {function} Callback when stroke end.
44369      */
44370     onEnd: false,
44371     
44372     getAutoCreate : function()
44373     {
44374         var cls = 'roo-signature column';
44375         
44376         if(this.cls){
44377             cls += ' ' + this.cls;
44378         }
44379         
44380         var col_sizes = [
44381             'lg',
44382             'md',
44383             'sm',
44384             'xs'
44385         ];
44386         
44387         for(var i = 0; i < col_sizes.length; i++) {
44388             if(this[col_sizes[i]]) {
44389                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44390             }
44391         }
44392         
44393         var cfg = {
44394             tag: 'div',
44395             cls: cls,
44396             cn: [
44397                 {
44398                     tag: 'div',
44399                     cls: 'roo-signature-body',
44400                     cn: [
44401                         {
44402                             tag: 'canvas',
44403                             cls: 'roo-signature-body-canvas',
44404                             height: this.canvas_height,
44405                             width: this.canvas_width
44406                         }
44407                     ]
44408                 },
44409                 {
44410                     tag: 'input',
44411                     type: 'file',
44412                     style: 'display: none'
44413                 }
44414             ]
44415         };
44416         
44417         return cfg;
44418     },
44419     
44420     initEvents: function() 
44421     {
44422         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44423         
44424         var canvas = this.canvasEl();
44425         
44426         // mouse && touch event swapping...
44427         canvas.dom.style.touchAction = 'none';
44428         canvas.dom.style.msTouchAction = 'none';
44429         
44430         this.mouse_btn_down = false;
44431         canvas.on('mousedown', this._handleMouseDown, this);
44432         canvas.on('mousemove', this._handleMouseMove, this);
44433         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44434         
44435         if (window.PointerEvent) {
44436             canvas.on('pointerdown', this._handleMouseDown, this);
44437             canvas.on('pointermove', this._handleMouseMove, this);
44438             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44439         }
44440         
44441         if ('ontouchstart' in window) {
44442             canvas.on('touchstart', this._handleTouchStart, this);
44443             canvas.on('touchmove', this._handleTouchMove, this);
44444             canvas.on('touchend', this._handleTouchEnd, this);
44445         }
44446         
44447         Roo.EventManager.onWindowResize(this.resize, this, true);
44448         
44449         // file input event
44450         this.fileEl().on('change', this.uploadImage, this);
44451         
44452         this.clear();
44453         
44454         this.resize();
44455     },
44456     
44457     resize: function(){
44458         
44459         var canvas = this.canvasEl().dom;
44460         var ctx = this.canvasElCtx();
44461         var img_data = false;
44462         
44463         if(canvas.width > 0) {
44464             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44465         }
44466         // setting canvas width will clean img data
44467         canvas.width = 0;
44468         
44469         var style = window.getComputedStyle ? 
44470             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44471             
44472         var padding_left = parseInt(style.paddingLeft) || 0;
44473         var padding_right = parseInt(style.paddingRight) || 0;
44474         
44475         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44476         
44477         if(img_data) {
44478             ctx.putImageData(img_data, 0, 0);
44479         }
44480     },
44481     
44482     _handleMouseDown: function(e)
44483     {
44484         if (e.browserEvent.which === 1) {
44485             this.mouse_btn_down = true;
44486             this.strokeBegin(e);
44487         }
44488     },
44489     
44490     _handleMouseMove: function (e)
44491     {
44492         if (this.mouse_btn_down) {
44493             this.strokeMoveUpdate(e);
44494         }
44495     },
44496     
44497     _handleMouseUp: function (e)
44498     {
44499         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44500             this.mouse_btn_down = false;
44501             this.strokeEnd(e);
44502         }
44503     },
44504     
44505     _handleTouchStart: function (e) {
44506         
44507         e.preventDefault();
44508         if (e.browserEvent.targetTouches.length === 1) {
44509             // var touch = e.browserEvent.changedTouches[0];
44510             // this.strokeBegin(touch);
44511             
44512              this.strokeBegin(e); // assume e catching the correct xy...
44513         }
44514     },
44515     
44516     _handleTouchMove: function (e) {
44517         e.preventDefault();
44518         // var touch = event.targetTouches[0];
44519         // _this._strokeMoveUpdate(touch);
44520         this.strokeMoveUpdate(e);
44521     },
44522     
44523     _handleTouchEnd: function (e) {
44524         var wasCanvasTouched = e.target === this.canvasEl().dom;
44525         if (wasCanvasTouched) {
44526             e.preventDefault();
44527             // var touch = event.changedTouches[0];
44528             // _this._strokeEnd(touch);
44529             this.strokeEnd(e);
44530         }
44531     },
44532     
44533     reset: function () {
44534         this._lastPoints = [];
44535         this._lastVelocity = 0;
44536         this._lastWidth = (this.min_width + this.max_width) / 2;
44537         this.canvasElCtx().fillStyle = this.dot_color;
44538     },
44539     
44540     strokeMoveUpdate: function(e)
44541     {
44542         this.strokeUpdate(e);
44543         
44544         if (this.throttle) {
44545             this.throttleStroke(this.strokeUpdate, this.throttle);
44546         }
44547         else {
44548             this.strokeUpdate(e);
44549         }
44550     },
44551     
44552     strokeBegin: function(e)
44553     {
44554         var newPointGroup = {
44555             color: this.dot_color,
44556             points: []
44557         };
44558         
44559         if (typeof this.onBegin === 'function') {
44560             this.onBegin(e);
44561         }
44562         
44563         this.curve_data.push(newPointGroup);
44564         this.reset();
44565         this.strokeUpdate(e);
44566     },
44567     
44568     strokeUpdate: function(e)
44569     {
44570         var rect = this.canvasEl().dom.getBoundingClientRect();
44571         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44572         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44573         var lastPoints = lastPointGroup.points;
44574         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44575         var isLastPointTooClose = lastPoint
44576             ? point.distanceTo(lastPoint) <= this.min_distance
44577             : false;
44578         var color = lastPointGroup.color;
44579         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44580             var curve = this.addPoint(point);
44581             if (!lastPoint) {
44582                 this.drawDot({color: color, point: point});
44583             }
44584             else if (curve) {
44585                 this.drawCurve({color: color, curve: curve});
44586             }
44587             lastPoints.push({
44588                 time: point.time,
44589                 x: point.x,
44590                 y: point.y
44591             });
44592         }
44593     },
44594     
44595     strokeEnd: function(e)
44596     {
44597         this.strokeUpdate(e);
44598         if (typeof this.onEnd === 'function') {
44599             this.onEnd(e);
44600         }
44601     },
44602     
44603     addPoint:  function (point) {
44604         var _lastPoints = this._lastPoints;
44605         _lastPoints.push(point);
44606         if (_lastPoints.length > 2) {
44607             if (_lastPoints.length === 3) {
44608                 _lastPoints.unshift(_lastPoints[0]);
44609             }
44610             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44611             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44612             _lastPoints.shift();
44613             return curve;
44614         }
44615         return null;
44616     },
44617     
44618     calculateCurveWidths: function (startPoint, endPoint) {
44619         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44620             (1 - this.velocity_filter_weight) * this._lastVelocity;
44621
44622         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44623         var widths = {
44624             end: newWidth,
44625             start: this._lastWidth
44626         };
44627         
44628         this._lastVelocity = velocity;
44629         this._lastWidth = newWidth;
44630         return widths;
44631     },
44632     
44633     drawDot: function (_a) {
44634         var color = _a.color, point = _a.point;
44635         var ctx = this.canvasElCtx();
44636         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44637         ctx.beginPath();
44638         this.drawCurveSegment(point.x, point.y, width);
44639         ctx.closePath();
44640         ctx.fillStyle = color;
44641         ctx.fill();
44642     },
44643     
44644     drawCurve: function (_a) {
44645         var color = _a.color, curve = _a.curve;
44646         var ctx = this.canvasElCtx();
44647         var widthDelta = curve.endWidth - curve.startWidth;
44648         var drawSteps = Math.floor(curve.length()) * 2;
44649         ctx.beginPath();
44650         ctx.fillStyle = color;
44651         for (var i = 0; i < drawSteps; i += 1) {
44652         var t = i / drawSteps;
44653         var tt = t * t;
44654         var ttt = tt * t;
44655         var u = 1 - t;
44656         var uu = u * u;
44657         var uuu = uu * u;
44658         var x = uuu * curve.startPoint.x;
44659         x += 3 * uu * t * curve.control1.x;
44660         x += 3 * u * tt * curve.control2.x;
44661         x += ttt * curve.endPoint.x;
44662         var y = uuu * curve.startPoint.y;
44663         y += 3 * uu * t * curve.control1.y;
44664         y += 3 * u * tt * curve.control2.y;
44665         y += ttt * curve.endPoint.y;
44666         var width = curve.startWidth + ttt * widthDelta;
44667         this.drawCurveSegment(x, y, width);
44668         }
44669         ctx.closePath();
44670         ctx.fill();
44671     },
44672     
44673     drawCurveSegment: function (x, y, width) {
44674         var ctx = this.canvasElCtx();
44675         ctx.moveTo(x, y);
44676         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44677         this.is_empty = false;
44678     },
44679     
44680     clear: function()
44681     {
44682         var ctx = this.canvasElCtx();
44683         var canvas = this.canvasEl().dom;
44684         ctx.fillStyle = this.bg_color;
44685         ctx.clearRect(0, 0, canvas.width, canvas.height);
44686         ctx.fillRect(0, 0, canvas.width, canvas.height);
44687         this.curve_data = [];
44688         this.reset();
44689         this.is_empty = true;
44690     },
44691     
44692     fileEl: function()
44693     {
44694         return  this.el.select('input',true).first();
44695     },
44696     
44697     canvasEl: function()
44698     {
44699         return this.el.select('canvas',true).first();
44700     },
44701     
44702     canvasElCtx: function()
44703     {
44704         return this.el.select('canvas',true).first().dom.getContext('2d');
44705     },
44706     
44707     getImage: function(type)
44708     {
44709         if(this.is_empty) {
44710             return false;
44711         }
44712         
44713         // encryption ?
44714         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44715     },
44716     
44717     drawFromImage: function(img_src)
44718     {
44719         var img = new Image();
44720         
44721         img.onload = function(){
44722             this.canvasElCtx().drawImage(img, 0, 0);
44723         }.bind(this);
44724         
44725         img.src = img_src;
44726         
44727         this.is_empty = false;
44728     },
44729     
44730     selectImage: function()
44731     {
44732         this.fileEl().dom.click();
44733     },
44734     
44735     uploadImage: function(e)
44736     {
44737         var reader = new FileReader();
44738         
44739         reader.onload = function(e){
44740             var img = new Image();
44741             img.onload = function(){
44742                 this.reset();
44743                 this.canvasElCtx().drawImage(img, 0, 0);
44744             }.bind(this);
44745             img.src = e.target.result;
44746         }.bind(this);
44747         
44748         reader.readAsDataURL(e.target.files[0]);
44749     },
44750     
44751     // Bezier Point Constructor
44752     Point: (function () {
44753         function Point(x, y, time) {
44754             this.x = x;
44755             this.y = y;
44756             this.time = time || Date.now();
44757         }
44758         Point.prototype.distanceTo = function (start) {
44759             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44760         };
44761         Point.prototype.equals = function (other) {
44762             return this.x === other.x && this.y === other.y && this.time === other.time;
44763         };
44764         Point.prototype.velocityFrom = function (start) {
44765             return this.time !== start.time
44766             ? this.distanceTo(start) / (this.time - start.time)
44767             : 0;
44768         };
44769         return Point;
44770     }()),
44771     
44772     
44773     // Bezier Constructor
44774     Bezier: (function () {
44775         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44776             this.startPoint = startPoint;
44777             this.control2 = control2;
44778             this.control1 = control1;
44779             this.endPoint = endPoint;
44780             this.startWidth = startWidth;
44781             this.endWidth = endWidth;
44782         }
44783         Bezier.fromPoints = function (points, widths, scope) {
44784             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44785             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44786             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44787         };
44788         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44789             var dx1 = s1.x - s2.x;
44790             var dy1 = s1.y - s2.y;
44791             var dx2 = s2.x - s3.x;
44792             var dy2 = s2.y - s3.y;
44793             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44794             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44795             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44796             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44797             var dxm = m1.x - m2.x;
44798             var dym = m1.y - m2.y;
44799             var k = l2 / (l1 + l2);
44800             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44801             var tx = s2.x - cm.x;
44802             var ty = s2.y - cm.y;
44803             return {
44804                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44805                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44806             };
44807         };
44808         Bezier.prototype.length = function () {
44809             var steps = 10;
44810             var length = 0;
44811             var px;
44812             var py;
44813             for (var i = 0; i <= steps; i += 1) {
44814                 var t = i / steps;
44815                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44816                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44817                 if (i > 0) {
44818                     var xdiff = cx - px;
44819                     var ydiff = cy - py;
44820                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44821                 }
44822                 px = cx;
44823                 py = cy;
44824             }
44825             return length;
44826         };
44827         Bezier.prototype.point = function (t, start, c1, c2, end) {
44828             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44829             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44830             + (3.0 * c2 * (1.0 - t) * t * t)
44831             + (end * t * t * t);
44832         };
44833         return Bezier;
44834     }()),
44835     
44836     throttleStroke: function(fn, wait) {
44837       if (wait === void 0) { wait = 250; }
44838       var previous = 0;
44839       var timeout = null;
44840       var result;
44841       var storedContext;
44842       var storedArgs;
44843       var later = function () {
44844           previous = Date.now();
44845           timeout = null;
44846           result = fn.apply(storedContext, storedArgs);
44847           if (!timeout) {
44848               storedContext = null;
44849               storedArgs = [];
44850           }
44851       };
44852       return function wrapper() {
44853           var args = [];
44854           for (var _i = 0; _i < arguments.length; _i++) {
44855               args[_i] = arguments[_i];
44856           }
44857           var now = Date.now();
44858           var remaining = wait - (now - previous);
44859           storedContext = this;
44860           storedArgs = args;
44861           if (remaining <= 0 || remaining > wait) {
44862               if (timeout) {
44863                   clearTimeout(timeout);
44864                   timeout = null;
44865               }
44866               previous = now;
44867               result = fn.apply(storedContext, storedArgs);
44868               if (!timeout) {
44869                   storedContext = null;
44870                   storedArgs = [];
44871               }
44872           }
44873           else if (!timeout) {
44874               timeout = window.setTimeout(later, remaining);
44875           }
44876           return result;
44877       };
44878   }
44879   
44880 });
44881
44882  
44883
44884