sort out zIndex and hunting for tooltips
[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     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3593     if (this.registerMenu && this.type != 'treeview')  {
3594         Roo.bootstrap.MenuMgr.register(this);
3595     }
3596     
3597     
3598     this.addEvents({
3599         /**
3600          * @event beforeshow
3601          * Fires before this menu is displayed (return false to block)
3602          * @param {Roo.menu.Menu} this
3603          */
3604         beforeshow : true,
3605         /**
3606          * @event beforehide
3607          * Fires before this menu is hidden (return false to block)
3608          * @param {Roo.menu.Menu} this
3609          */
3610         beforehide : true,
3611         /**
3612          * @event show
3613          * Fires after this menu is displayed
3614          * @param {Roo.menu.Menu} this
3615          */
3616         show : true,
3617         /**
3618          * @event hide
3619          * Fires after this menu is hidden
3620          * @param {Roo.menu.Menu} this
3621          */
3622         hide : true,
3623         /**
3624          * @event click
3625          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3626          * @param {Roo.menu.Menu} this
3627          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628          * @param {Roo.EventObject} e
3629          */
3630         click : true,
3631         /**
3632          * @event mouseover
3633          * Fires when the mouse is hovering over this menu
3634          * @param {Roo.menu.Menu} this
3635          * @param {Roo.EventObject} e
3636          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3637          */
3638         mouseover : true,
3639         /**
3640          * @event mouseout
3641          * Fires when the mouse exits this menu
3642          * @param {Roo.menu.Menu} this
3643          * @param {Roo.EventObject} e
3644          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3645          */
3646         mouseout : true,
3647         /**
3648          * @event itemclick
3649          * Fires when a menu item contained in this menu is clicked
3650          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3651          * @param {Roo.EventObject} e
3652          */
3653         itemclick: true
3654     });
3655     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3656 };
3657
3658 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3659     
3660    /// html : false,
3661    
3662     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3663     type: false,
3664     /**
3665      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3666      */
3667     registerMenu : true,
3668     
3669     menuItems :false, // stores the menu items..
3670     
3671     hidden:true,
3672         
3673     parentMenu : false,
3674     
3675     stopEvent : true,
3676     
3677     isLink : false,
3678     
3679     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3680     
3681     hideTrigger : false,
3682     
3683     align : 'tl-bl?',
3684     
3685     
3686     getChildContainer : function() {
3687         return this.el;  
3688     },
3689     
3690     getAutoCreate : function(){
3691          
3692         //if (['right'].indexOf(this.align)!==-1) {
3693         //    cfg.cn[1].cls += ' pull-right'
3694         //}
3695          
3696         var cfg = {
3697             tag : 'ul',
3698             cls : 'dropdown-menu shadow' ,
3699             style : 'z-index:1000'
3700             
3701         };
3702         
3703         if (this.type === 'submenu') {
3704             cfg.cls = 'submenu active';
3705         }
3706         if (this.type === 'treeview') {
3707             cfg.cls = 'treeview-menu';
3708         }
3709         
3710         return cfg;
3711     },
3712     initEvents : function() {
3713         
3714        // Roo.log("ADD event");
3715        // Roo.log(this.triggerEl.dom);
3716         
3717         this.triggerEl.on('click', this.onTriggerClick, this);
3718         
3719         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3720         
3721         if (!this.hideTrigger) {
3722             if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3723                 // dropdown toggle on the 'a' in BS4?
3724                 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3725             } else {
3726                 this.triggerEl.addClass('dropdown-toggle');
3727             }
3728         }
3729         if (Roo.isTouch) {
3730             this.el.on('touchstart'  , this.onTouch, this);
3731         }
3732         this.el.on('click' , this.onClick, this);
3733
3734         this.el.on("mouseover", this.onMouseOver, this);
3735         this.el.on("mouseout", this.onMouseOut, this);
3736         
3737     },
3738     
3739     findTargetItem : function(e)
3740     {
3741         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3742         if(!t){
3743             return false;
3744         }
3745         //Roo.log(t);         Roo.log(t.id);
3746         if(t && t.id){
3747             //Roo.log(this.menuitems);
3748             return this.menuitems.get(t.id);
3749             
3750             //return this.items.get(t.menuItemId);
3751         }
3752         
3753         return false;
3754     },
3755     
3756     onTouch : function(e) 
3757     {
3758         Roo.log("menu.onTouch");
3759         //e.stopEvent(); this make the user popdown broken
3760         this.onClick(e);
3761     },
3762     
3763     onClick : function(e)
3764     {
3765         Roo.log("menu.onClick");
3766         
3767         var t = this.findTargetItem(e);
3768         if(!t || t.isContainer){
3769             return;
3770         }
3771         Roo.log(e);
3772         /*
3773         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3774             if(t == this.activeItem && t.shouldDeactivate(e)){
3775                 this.activeItem.deactivate();
3776                 delete this.activeItem;
3777                 return;
3778             }
3779             if(t.canActivate){
3780                 this.setActiveItem(t, true);
3781             }
3782             return;
3783             
3784             
3785         }
3786         */
3787        
3788         Roo.log('pass click event');
3789         
3790         t.onClick(e);
3791         
3792         this.fireEvent("click", this, t, e);
3793         
3794         var _this = this;
3795         
3796         if(!t.href.length || t.href == '#'){
3797             (function() { _this.hide(); }).defer(100);
3798         }
3799         
3800     },
3801     
3802     onMouseOver : function(e){
3803         var t  = this.findTargetItem(e);
3804         //Roo.log(t);
3805         //if(t){
3806         //    if(t.canActivate && !t.disabled){
3807         //        this.setActiveItem(t, true);
3808         //    }
3809         //}
3810         
3811         this.fireEvent("mouseover", this, e, t);
3812     },
3813     isVisible : function(){
3814         return !this.hidden;
3815     },
3816     onMouseOut : function(e){
3817         var t  = this.findTargetItem(e);
3818         
3819         //if(t ){
3820         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3821         //        this.activeItem.deactivate();
3822         //        delete this.activeItem;
3823         //    }
3824         //}
3825         this.fireEvent("mouseout", this, e, t);
3826     },
3827     
3828     
3829     /**
3830      * Displays this menu relative to another element
3831      * @param {String/HTMLElement/Roo.Element} element The element to align to
3832      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3833      * the element (defaults to this.defaultAlign)
3834      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3835      */
3836     show : function(el, pos, parentMenu)
3837     {
3838         if (false === this.fireEvent("beforeshow", this)) {
3839             Roo.log("show canceled");
3840             return;
3841         }
3842         this.parentMenu = parentMenu;
3843         if(!this.el){
3844             this.render();
3845         }
3846         this.el.addClass('show'); // show otherwise we do not know how big we are..
3847          
3848         var xy = this.el.getAlignToXY(el, pos);
3849         
3850         // bl-tl << left align  below
3851         // tl-bl << left align 
3852         
3853         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3854             // if it goes to far to the right.. -> align left.
3855             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3856         }
3857         if(xy[0] < 0){
3858             // was left align - go right?
3859             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3860         }
3861         
3862         // goes down the bottom
3863         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3864            xy[1]  < 0 ){
3865             var a = this.align.replace('?', '').split('-');
3866             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3867             
3868         }
3869         
3870         this.showAt(  xy , parentMenu, false);
3871     },
3872      /**
3873      * Displays this menu at a specific xy position
3874      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3875      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3876      */
3877     showAt : function(xy, parentMenu, /* private: */_e){
3878         this.parentMenu = parentMenu;
3879         if(!this.el){
3880             this.render();
3881         }
3882         if(_e !== false){
3883             this.fireEvent("beforeshow", this);
3884             //xy = this.el.adjustForConstraints(xy);
3885         }
3886         
3887         //this.el.show();
3888         this.hideMenuItems();
3889         this.hidden = false;
3890         this.triggerEl.addClass('open');
3891         this.el.addClass('show');
3892         
3893         
3894         
3895         // reassign x when hitting right
3896         
3897         // reassign y when hitting bottom
3898         
3899         // but the list may align on trigger left or trigger top... should it be a properity?
3900         
3901         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3902             this.el.setXY(xy);
3903         }
3904         
3905         this.focus();
3906         this.fireEvent("show", this);
3907     },
3908     
3909     focus : function(){
3910         return;
3911         if(!this.hidden){
3912             this.doFocus.defer(50, this);
3913         }
3914     },
3915
3916     doFocus : function(){
3917         if(!this.hidden){
3918             this.focusEl.focus();
3919         }
3920     },
3921
3922     /**
3923      * Hides this menu and optionally all parent menus
3924      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3925      */
3926     hide : function(deep)
3927     {
3928         if (false === this.fireEvent("beforehide", this)) {
3929             Roo.log("hide canceled");
3930             return;
3931         }
3932         this.hideMenuItems();
3933         if(this.el && this.isVisible()){
3934            
3935             if(this.activeItem){
3936                 this.activeItem.deactivate();
3937                 this.activeItem = null;
3938             }
3939             this.triggerEl.removeClass('open');;
3940             this.el.removeClass('show');
3941             this.hidden = true;
3942             this.fireEvent("hide", this);
3943         }
3944         if(deep === true && this.parentMenu){
3945             this.parentMenu.hide(true);
3946         }
3947     },
3948     
3949     onTriggerClick : function(e)
3950     {
3951         Roo.log('trigger click');
3952         
3953         var target = e.getTarget();
3954         
3955         Roo.log(target.nodeName.toLowerCase());
3956         
3957         if(target.nodeName.toLowerCase() === 'i'){
3958             e.preventDefault();
3959         }
3960         
3961     },
3962     
3963     onTriggerPress  : function(e)
3964     {
3965         Roo.log('trigger press');
3966         //Roo.log(e.getTarget());
3967        // Roo.log(this.triggerEl.dom);
3968        
3969         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3970         var pel = Roo.get(e.getTarget());
3971         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3972             Roo.log('is treeview or dropdown?');
3973             return;
3974         }
3975         
3976         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3977             return;
3978         }
3979         
3980         if (this.isVisible()) {
3981             Roo.log('hide');
3982             this.hide();
3983         } else {
3984             Roo.log('show');
3985              
3986             this.show(this.triggerEl, this.align, false);
3987         }
3988         
3989         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3990             e.stopEvent();
3991         }
3992         
3993     },
3994        
3995     
3996     hideMenuItems : function()
3997     {
3998         Roo.log("hide Menu Items");
3999         if (!this.el) { 
4000             return;
4001         }
4002         
4003         this.el.select('.open',true).each(function(aa) {
4004             
4005             aa.removeClass('open');
4006          
4007         });
4008     },
4009     addxtypeChild : function (tree, cntr) {
4010         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4011           
4012         this.menuitems.add(comp);
4013         return comp;
4014
4015     },
4016     getEl : function()
4017     {
4018         Roo.log(this.el);
4019         return this.el;
4020     },
4021     
4022     clear : function()
4023     {
4024         this.getEl().dom.innerHTML = '';
4025         this.menuitems.clear();
4026     }
4027 });
4028
4029  
4030  /*
4031  * - LGPL
4032  *
4033  * menu item
4034  * 
4035  */
4036
4037
4038 /**
4039  * @class Roo.bootstrap.MenuItem
4040  * @extends Roo.bootstrap.Component
4041  * Bootstrap MenuItem class
4042  * @cfg {String} html the menu label
4043  * @cfg {String} href the link
4044  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4045  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4046  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4047  * @cfg {String} fa favicon to show on left of menu item.
4048  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4049  * 
4050  * 
4051  * @constructor
4052  * Create a new MenuItem
4053  * @param {Object} config The config object
4054  */
4055
4056
4057 Roo.bootstrap.MenuItem = function(config){
4058     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4059     this.addEvents({
4060         // raw events
4061         /**
4062          * @event click
4063          * The raw click event for the entire grid.
4064          * @param {Roo.bootstrap.MenuItem} this
4065          * @param {Roo.EventObject} e
4066          */
4067         "click" : true
4068     });
4069 };
4070
4071 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4072     
4073     href : false,
4074     html : false,
4075     preventDefault: false,
4076     isContainer : false,
4077     active : false,
4078     fa: false,
4079     
4080     getAutoCreate : function(){
4081         
4082         if(this.isContainer){
4083             return {
4084                 tag: 'li',
4085                 cls: 'dropdown-menu-item '
4086             };
4087         }
4088         var ctag = {
4089             tag: 'span',
4090             html: 'Link'
4091         };
4092         
4093         var anc = {
4094             tag : 'a',
4095             cls : 'dropdown-item',
4096             href : '#',
4097             cn : [  ]
4098         };
4099         
4100         if (this.fa !== false) {
4101             anc.cn.push({
4102                 tag : 'i',
4103                 cls : 'fa fa-' + this.fa
4104             });
4105         }
4106         
4107         anc.cn.push(ctag);
4108         
4109         
4110         var cfg= {
4111             tag: 'li',
4112             cls: 'dropdown-menu-item',
4113             cn: [ anc ]
4114         };
4115         if (this.parent().type == 'treeview') {
4116             cfg.cls = 'treeview-menu';
4117         }
4118         if (this.active) {
4119             cfg.cls += ' active';
4120         }
4121         
4122         
4123         
4124         anc.href = this.href || cfg.cn[0].href ;
4125         ctag.html = this.html || cfg.cn[0].html ;
4126         return cfg;
4127     },
4128     
4129     initEvents: function()
4130     {
4131         if (this.parent().type == 'treeview') {
4132             this.el.select('a').on('click', this.onClick, this);
4133         }
4134         
4135         if (this.menu) {
4136             this.menu.parentType = this.xtype;
4137             this.menu.triggerEl = this.el;
4138             this.menu = this.addxtype(Roo.apply({}, this.menu));
4139         }
4140         
4141     },
4142     onClick : function(e)
4143     {
4144         Roo.log('item on click ');
4145         
4146         if(this.preventDefault){
4147             e.preventDefault();
4148         }
4149         //this.parent().hideMenuItems();
4150         
4151         this.fireEvent('click', this, e);
4152     },
4153     getEl : function()
4154     {
4155         return this.el;
4156     } 
4157 });
4158
4159  
4160
4161  /*
4162  * - LGPL
4163  *
4164  * menu separator
4165  * 
4166  */
4167
4168
4169 /**
4170  * @class Roo.bootstrap.MenuSeparator
4171  * @extends Roo.bootstrap.Component
4172  * Bootstrap MenuSeparator class
4173  * 
4174  * @constructor
4175  * Create a new MenuItem
4176  * @param {Object} config The config object
4177  */
4178
4179
4180 Roo.bootstrap.MenuSeparator = function(config){
4181     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4182 };
4183
4184 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4185     
4186     getAutoCreate : function(){
4187         var cfg = {
4188             cls: 'divider',
4189             tag : 'li'
4190         };
4191         
4192         return cfg;
4193     }
4194    
4195 });
4196
4197  
4198
4199  
4200 /*
4201 * Licence: LGPL
4202 */
4203
4204 /**
4205  * @class Roo.bootstrap.Modal
4206  * @extends Roo.bootstrap.Component
4207  * Bootstrap Modal class
4208  * @cfg {String} title Title of dialog
4209  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4210  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4211  * @cfg {Boolean} specificTitle default false
4212  * @cfg {Array} buttons Array of buttons or standard button set..
4213  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4214  * @cfg {Boolean} animate default true
4215  * @cfg {Boolean} allow_close default true
4216  * @cfg {Boolean} fitwindow default false
4217  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4218  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4219  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4220  * @cfg {String} size (sm|lg|xl) default empty
4221  * @cfg {Number} max_width set the max width of modal
4222  * @cfg {Boolean} editableTitle can the title be edited
4223
4224  *
4225  *
4226  * @constructor
4227  * Create a new Modal Dialog
4228  * @param {Object} config The config object
4229  */
4230
4231 Roo.bootstrap.Modal = function(config){
4232     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4233     this.addEvents({
4234         // raw events
4235         /**
4236          * @event btnclick
4237          * The raw btnclick event for the button
4238          * @param {Roo.EventObject} e
4239          */
4240         "btnclick" : true,
4241         /**
4242          * @event resize
4243          * Fire when dialog resize
4244          * @param {Roo.bootstrap.Modal} this
4245          * @param {Roo.EventObject} e
4246          */
4247         "resize" : true,
4248         /**
4249          * @event titlechanged
4250          * Fire when the editable title has been changed
4251          * @param {Roo.bootstrap.Modal} this
4252          * @param {Roo.EventObject} value
4253          */
4254         "titlechanged" : true 
4255         
4256     });
4257     this.buttons = this.buttons || [];
4258
4259     if (this.tmpl) {
4260         this.tmpl = Roo.factory(this.tmpl);
4261     }
4262
4263 };
4264
4265 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4266
4267     title : 'test dialog',
4268
4269     buttons : false,
4270
4271     // set on load...
4272
4273     html: false,
4274
4275     tmp: false,
4276
4277     specificTitle: false,
4278
4279     buttonPosition: 'right',
4280
4281     allow_close : true,
4282
4283     animate : true,
4284
4285     fitwindow: false,
4286     
4287      // private
4288     dialogEl: false,
4289     bodyEl:  false,
4290     footerEl:  false,
4291     titleEl:  false,
4292     closeEl:  false,
4293
4294     size: '',
4295     
4296     max_width: 0,
4297     
4298     max_height: 0,
4299     
4300     fit_content: false,
4301     editableTitle  : false,
4302
4303     onRender : function(ct, position)
4304     {
4305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4306
4307         if(!this.el){
4308             var cfg = Roo.apply({},  this.getAutoCreate());
4309             cfg.id = Roo.id();
4310             //if(!cfg.name){
4311             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4312             //}
4313             //if (!cfg.name.length) {
4314             //    delete cfg.name;
4315            // }
4316             if (this.cls) {
4317                 cfg.cls += ' ' + this.cls;
4318             }
4319             if (this.style) {
4320                 cfg.style = this.style;
4321             }
4322             this.el = Roo.get(document.body).createChild(cfg, position);
4323         }
4324         //var type = this.el.dom.type;
4325
4326
4327         if(this.tabIndex !== undefined){
4328             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4329         }
4330
4331         this.dialogEl = this.el.select('.modal-dialog',true).first();
4332         this.bodyEl = this.el.select('.modal-body',true).first();
4333         this.closeEl = this.el.select('.modal-header .close', true).first();
4334         this.headerEl = this.el.select('.modal-header',true).first();
4335         this.titleEl = this.el.select('.modal-title',true).first();
4336         this.footerEl = this.el.select('.modal-footer',true).first();
4337
4338         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4339         
4340         //this.el.addClass("x-dlg-modal");
4341
4342         if (this.buttons.length) {
4343             Roo.each(this.buttons, function(bb) {
4344                 var b = Roo.apply({}, bb);
4345                 b.xns = b.xns || Roo.bootstrap;
4346                 b.xtype = b.xtype || 'Button';
4347                 if (typeof(b.listeners) == 'undefined') {
4348                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4349                 }
4350
4351                 var btn = Roo.factory(b);
4352
4353                 btn.render(this.getButtonContainer());
4354
4355             },this);
4356         }
4357         // render the children.
4358         var nitems = [];
4359
4360         if(typeof(this.items) != 'undefined'){
4361             var items = this.items;
4362             delete this.items;
4363
4364             for(var i =0;i < items.length;i++) {
4365                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4366             }
4367         }
4368
4369         this.items = nitems;
4370
4371         // where are these used - they used to be body/close/footer
4372
4373
4374         this.initEvents();
4375         //this.el.addClass([this.fieldClass, this.cls]);
4376
4377     },
4378
4379     getAutoCreate : function()
4380     {
4381         // we will default to modal-body-overflow - might need to remove or make optional later.
4382         var bdy = {
4383                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4384                 html : this.html || ''
4385         };
4386
4387         var title = {
4388             tag: 'h5',
4389             cls : 'modal-title',
4390             html : this.title
4391         };
4392
4393         if(this.specificTitle){ // WTF is this?
4394             title = this.title;
4395         }
4396
4397         var header = [];
4398         if (this.allow_close && Roo.bootstrap.version == 3) {
4399             header.push({
4400                 tag: 'button',
4401                 cls : 'close',
4402                 html : '&times'
4403             });
4404         }
4405
4406         header.push(title);
4407
4408         if (this.editableTitle) {
4409             header.push({
4410                 cls: 'form-control roo-editable-title d-none',
4411                 tag: 'input',
4412                 type: 'text'
4413             });
4414         }
4415         
4416         if (this.allow_close && Roo.bootstrap.version == 4) {
4417             header.push({
4418                 tag: 'button',
4419                 cls : 'close',
4420                 html : '&times'
4421             });
4422         }
4423         
4424         var size = '';
4425
4426         if(this.size.length){
4427             size = 'modal-' + this.size;
4428         }
4429         
4430         var footer = Roo.bootstrap.version == 3 ?
4431             {
4432                 cls : 'modal-footer',
4433                 cn : [
4434                     {
4435                         tag: 'div',
4436                         cls: 'btn-' + this.buttonPosition
4437                     }
4438                 ]
4439
4440             } :
4441             {  // BS4 uses mr-auto on left buttons....
4442                 cls : 'modal-footer'
4443             };
4444
4445             
4446
4447         
4448         
4449         var modal = {
4450             cls: "modal",
4451              cn : [
4452                 {
4453                     cls: "modal-dialog " + size,
4454                     cn : [
4455                         {
4456                             cls : "modal-content",
4457                             cn : [
4458                                 {
4459                                     cls : 'modal-header',
4460                                     cn : header
4461                                 },
4462                                 bdy,
4463                                 footer
4464                             ]
4465
4466                         }
4467                     ]
4468
4469                 }
4470             ]
4471         };
4472
4473         if(this.animate){
4474             modal.cls += ' fade';
4475         }
4476
4477         return modal;
4478
4479     },
4480     getChildContainer : function() {
4481
4482          return this.bodyEl;
4483
4484     },
4485     getButtonContainer : function() {
4486         
4487          return Roo.bootstrap.version == 4 ?
4488             this.el.select('.modal-footer',true).first()
4489             : this.el.select('.modal-footer div',true).first();
4490
4491     },
4492     initEvents : function()
4493     {
4494         if (this.allow_close) {
4495             this.closeEl.on('click', this.hide, this);
4496         }
4497         Roo.EventManager.onWindowResize(this.resize, this, true);
4498         if (this.editableTitle) {
4499             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4500             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4501             this.headerEditEl.on('keyup', function(e) {
4502                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4503                         this.toggleHeaderInput(false)
4504                     }
4505                 }, this);
4506             this.headerEditEl.on('blur', function(e) {
4507                 this.toggleHeaderInput(false)
4508             },this);
4509         }
4510
4511     },
4512   
4513
4514     resize : function()
4515     {
4516         this.maskEl.setSize(
4517             Roo.lib.Dom.getViewWidth(true),
4518             Roo.lib.Dom.getViewHeight(true)
4519         );
4520         
4521         if (this.fitwindow) {
4522             
4523            this.dialogEl.setStyle( { 'max-width' : '100%' });
4524             this.setSize(
4525                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4526                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4527             );
4528             return;
4529         }
4530         
4531         if(this.max_width !== 0) {
4532             
4533             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4534             
4535             if(this.height) {
4536                 this.setSize(w, this.height);
4537                 return;
4538             }
4539             
4540             if(this.max_height) {
4541                 this.setSize(w,Math.min(
4542                     this.max_height,
4543                     Roo.lib.Dom.getViewportHeight(true) - 60
4544                 ));
4545                 
4546                 return;
4547             }
4548             
4549             if(!this.fit_content) {
4550                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4551                 return;
4552             }
4553             
4554             this.setSize(w, Math.min(
4555                 60 +
4556                 this.headerEl.getHeight() + 
4557                 this.footerEl.getHeight() + 
4558                 this.getChildHeight(this.bodyEl.dom.childNodes),
4559                 Roo.lib.Dom.getViewportHeight(true) - 60)
4560             );
4561         }
4562         
4563     },
4564
4565     setSize : function(w,h)
4566     {
4567         if (!w && !h) {
4568             return;
4569         }
4570         
4571         this.resizeTo(w,h);
4572     },
4573
4574     show : function() {
4575
4576         if (!this.rendered) {
4577             this.render();
4578         }
4579         this.toggleHeaderInput(false);
4580         //this.el.setStyle('display', 'block');
4581         this.el.removeClass('hideing');
4582         this.el.dom.style.display='block';
4583         
4584         Roo.get(document.body).addClass('modal-open');
4585  
4586         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4587             
4588             (function(){
4589                 this.el.addClass('show');
4590                 this.el.addClass('in');
4591             }).defer(50, this);
4592         }else{
4593             this.el.addClass('show');
4594             this.el.addClass('in');
4595         }
4596
4597         // not sure how we can show data in here..
4598         //if (this.tmpl) {
4599         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4600         //}
4601
4602         Roo.get(document.body).addClass("x-body-masked");
4603         
4604         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4605         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4606         this.maskEl.dom.style.display = 'block';
4607         this.maskEl.addClass('show');
4608         
4609         
4610         this.resize();
4611         
4612         this.fireEvent('show', this);
4613
4614         // set zindex here - otherwise it appears to be ignored...
4615         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4616
4617         (function () {
4618             this.items.forEach( function(e) {
4619                 e.layout ? e.layout() : false;
4620
4621             });
4622         }).defer(100,this);
4623
4624     },
4625     hide : function()
4626     {
4627         if(this.fireEvent("beforehide", this) !== false){
4628             
4629             this.maskEl.removeClass('show');
4630             
4631             this.maskEl.dom.style.display = '';
4632             Roo.get(document.body).removeClass("x-body-masked");
4633             this.el.removeClass('in');
4634             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4635
4636             if(this.animate){ // why
4637                 this.el.addClass('hideing');
4638                 this.el.removeClass('show');
4639                 (function(){
4640                     if (!this.el.hasClass('hideing')) {
4641                         return; // it's been shown again...
4642                     }
4643                     
4644                     this.el.dom.style.display='';
4645
4646                     Roo.get(document.body).removeClass('modal-open');
4647                     this.el.removeClass('hideing');
4648                 }).defer(150,this);
4649                 
4650             }else{
4651                 this.el.removeClass('show');
4652                 this.el.dom.style.display='';
4653                 Roo.get(document.body).removeClass('modal-open');
4654
4655             }
4656             this.fireEvent('hide', this);
4657         }
4658     },
4659     isVisible : function()
4660     {
4661         
4662         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4663         
4664     },
4665
4666     addButton : function(str, cb)
4667     {
4668
4669
4670         var b = Roo.apply({}, { html : str } );
4671         b.xns = b.xns || Roo.bootstrap;
4672         b.xtype = b.xtype || 'Button';
4673         if (typeof(b.listeners) == 'undefined') {
4674             b.listeners = { click : cb.createDelegate(this)  };
4675         }
4676
4677         var btn = Roo.factory(b);
4678
4679         btn.render(this.getButtonContainer());
4680
4681         return btn;
4682
4683     },
4684
4685     setDefaultButton : function(btn)
4686     {
4687         //this.el.select('.modal-footer').()
4688     },
4689
4690     resizeTo: function(w,h)
4691     {
4692         this.dialogEl.setWidth(w);
4693         
4694         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4695
4696         this.bodyEl.setHeight(h - diff);
4697         
4698         this.fireEvent('resize', this);
4699     },
4700     
4701     setContentSize  : function(w, h)
4702     {
4703
4704     },
4705     onButtonClick: function(btn,e)
4706     {
4707         //Roo.log([a,b,c]);
4708         this.fireEvent('btnclick', btn.name, e);
4709     },
4710      /**
4711      * Set the title of the Dialog
4712      * @param {String} str new Title
4713      */
4714     setTitle: function(str) {
4715         this.titleEl.dom.innerHTML = str;
4716         this.title = str;
4717     },
4718     /**
4719      * Set the body of the Dialog
4720      * @param {String} str new Title
4721      */
4722     setBody: function(str) {
4723         this.bodyEl.dom.innerHTML = str;
4724     },
4725     /**
4726      * Set the body of the Dialog using the template
4727      * @param {Obj} data - apply this data to the template and replace the body contents.
4728      */
4729     applyBody: function(obj)
4730     {
4731         if (!this.tmpl) {
4732             Roo.log("Error - using apply Body without a template");
4733             //code
4734         }
4735         this.tmpl.overwrite(this.bodyEl, obj);
4736     },
4737     
4738     getChildHeight : function(child_nodes)
4739     {
4740         if(
4741             !child_nodes ||
4742             child_nodes.length == 0
4743         ) {
4744             return 0;
4745         }
4746         
4747         var child_height = 0;
4748         
4749         for(var i = 0; i < child_nodes.length; i++) {
4750             
4751             /*
4752             * for modal with tabs...
4753             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4754                 
4755                 var layout_childs = child_nodes[i].childNodes;
4756                 
4757                 for(var j = 0; j < layout_childs.length; j++) {
4758                     
4759                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4760                         
4761                         var layout_body_childs = layout_childs[j].childNodes;
4762                         
4763                         for(var k = 0; k < layout_body_childs.length; k++) {
4764                             
4765                             if(layout_body_childs[k].classList.contains('navbar')) {
4766                                 child_height += layout_body_childs[k].offsetHeight;
4767                                 continue;
4768                             }
4769                             
4770                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4771                                 
4772                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4773                                 
4774                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4775                                     
4776                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4777                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4778                                         continue;
4779                                     }
4780                                     
4781                                 }
4782                                 
4783                             }
4784                             
4785                         }
4786                     }
4787                 }
4788                 continue;
4789             }
4790             */
4791             
4792             child_height += child_nodes[i].offsetHeight;
4793             // Roo.log(child_nodes[i].offsetHeight);
4794         }
4795         
4796         return child_height;
4797     },
4798     toggleHeaderInput : function(is_edit)
4799     {
4800         if (!this.editableTitle) {
4801             return; // not editable.
4802         }
4803         if (is_edit && this.is_header_editing) {
4804             return; // already editing..
4805         }
4806         if (is_edit) {
4807     
4808             this.headerEditEl.dom.value = this.title;
4809             this.headerEditEl.removeClass('d-none');
4810             this.headerEditEl.dom.focus();
4811             this.titleEl.addClass('d-none');
4812             
4813             this.is_header_editing = true;
4814             return
4815         }
4816         // flip back to not editing.
4817         this.title = this.headerEditEl.dom.value;
4818         this.headerEditEl.addClass('d-none');
4819         this.titleEl.removeClass('d-none');
4820         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4821         this.is_header_editing = false;
4822         this.fireEvent('titlechanged', this, this.title);
4823     
4824             
4825         
4826     }
4827
4828 });
4829
4830
4831 Roo.apply(Roo.bootstrap.Modal,  {
4832     /**
4833          * Button config that displays a single OK button
4834          * @type Object
4835          */
4836         OK :  [{
4837             name : 'ok',
4838             weight : 'primary',
4839             html : 'OK'
4840         }],
4841         /**
4842          * Button config that displays Yes and No buttons
4843          * @type Object
4844          */
4845         YESNO : [
4846             {
4847                 name  : 'no',
4848                 html : 'No'
4849             },
4850             {
4851                 name  :'yes',
4852                 weight : 'primary',
4853                 html : 'Yes'
4854             }
4855         ],
4856
4857         /**
4858          * Button config that displays OK and Cancel buttons
4859          * @type Object
4860          */
4861         OKCANCEL : [
4862             {
4863                name : 'cancel',
4864                 html : 'Cancel'
4865             },
4866             {
4867                 name : 'ok',
4868                 weight : 'primary',
4869                 html : 'OK'
4870             }
4871         ],
4872         /**
4873          * Button config that displays Yes, No and Cancel buttons
4874          * @type Object
4875          */
4876         YESNOCANCEL : [
4877             {
4878                 name : 'yes',
4879                 weight : 'primary',
4880                 html : 'Yes'
4881             },
4882             {
4883                 name : 'no',
4884                 html : 'No'
4885             },
4886             {
4887                 name : 'cancel',
4888                 html : 'Cancel'
4889             }
4890         ],
4891         
4892         zIndex : 10001
4893 });
4894
4895 /*
4896  * - LGPL
4897  *
4898  * messagebox - can be used as a replace
4899  * 
4900  */
4901 /**
4902  * @class Roo.MessageBox
4903  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4904  * Example usage:
4905  *<pre><code>
4906 // Basic alert:
4907 Roo.Msg.alert('Status', 'Changes saved successfully.');
4908
4909 // Prompt for user data:
4910 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4911     if (btn == 'ok'){
4912         // process text value...
4913     }
4914 });
4915
4916 // Show a dialog using config options:
4917 Roo.Msg.show({
4918    title:'Save Changes?',
4919    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4920    buttons: Roo.Msg.YESNOCANCEL,
4921    fn: processResult,
4922    animEl: 'elId'
4923 });
4924 </code></pre>
4925  * @singleton
4926  */
4927 Roo.bootstrap.MessageBox = function(){
4928     var dlg, opt, mask, waitTimer;
4929     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4930     var buttons, activeTextEl, bwidth;
4931
4932     
4933     // private
4934     var handleButton = function(button){
4935         dlg.hide();
4936         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4937     };
4938
4939     // private
4940     var handleHide = function(){
4941         if(opt && opt.cls){
4942             dlg.el.removeClass(opt.cls);
4943         }
4944         //if(waitTimer){
4945         //    Roo.TaskMgr.stop(waitTimer);
4946         //    waitTimer = null;
4947         //}
4948     };
4949
4950     // private
4951     var updateButtons = function(b){
4952         var width = 0;
4953         if(!b){
4954             buttons["ok"].hide();
4955             buttons["cancel"].hide();
4956             buttons["yes"].hide();
4957             buttons["no"].hide();
4958             dlg.footerEl.hide();
4959             
4960             return width;
4961         }
4962         dlg.footerEl.show();
4963         for(var k in buttons){
4964             if(typeof buttons[k] != "function"){
4965                 if(b[k]){
4966                     buttons[k].show();
4967                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4968                     width += buttons[k].el.getWidth()+15;
4969                 }else{
4970                     buttons[k].hide();
4971                 }
4972             }
4973         }
4974         return width;
4975     };
4976
4977     // private
4978     var handleEsc = function(d, k, e){
4979         if(opt && opt.closable !== false){
4980             dlg.hide();
4981         }
4982         if(e){
4983             e.stopEvent();
4984         }
4985     };
4986
4987     return {
4988         /**
4989          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4990          * @return {Roo.BasicDialog} The BasicDialog element
4991          */
4992         getDialog : function(){
4993            if(!dlg){
4994                 dlg = new Roo.bootstrap.Modal( {
4995                     //draggable: true,
4996                     //resizable:false,
4997                     //constraintoviewport:false,
4998                     //fixedcenter:true,
4999                     //collapsible : false,
5000                     //shim:true,
5001                     //modal: true,
5002                 //    width: 'auto',
5003                   //  height:100,
5004                     //buttonAlign:"center",
5005                     closeClick : function(){
5006                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5007                             handleButton("no");
5008                         }else{
5009                             handleButton("cancel");
5010                         }
5011                     }
5012                 });
5013                 dlg.render();
5014                 dlg.on("hide", handleHide);
5015                 mask = dlg.mask;
5016                 //dlg.addKeyListener(27, handleEsc);
5017                 buttons = {};
5018                 this.buttons = buttons;
5019                 var bt = this.buttonText;
5020                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5021                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5022                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5023                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5024                 //Roo.log(buttons);
5025                 bodyEl = dlg.bodyEl.createChild({
5026
5027                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5028                         '<textarea class="roo-mb-textarea"></textarea>' +
5029                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5030                 });
5031                 msgEl = bodyEl.dom.firstChild;
5032                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5033                 textboxEl.enableDisplayMode();
5034                 textboxEl.addKeyListener([10,13], function(){
5035                     if(dlg.isVisible() && opt && opt.buttons){
5036                         if(opt.buttons.ok){
5037                             handleButton("ok");
5038                         }else if(opt.buttons.yes){
5039                             handleButton("yes");
5040                         }
5041                     }
5042                 });
5043                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5044                 textareaEl.enableDisplayMode();
5045                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5046                 progressEl.enableDisplayMode();
5047                 
5048                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5049                 var pf = progressEl.dom.firstChild;
5050                 if (pf) {
5051                     pp = Roo.get(pf.firstChild);
5052                     pp.setHeight(pf.offsetHeight);
5053                 }
5054                 
5055             }
5056             return dlg;
5057         },
5058
5059         /**
5060          * Updates the message box body text
5061          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5062          * the XHTML-compliant non-breaking space character '&amp;#160;')
5063          * @return {Roo.MessageBox} This message box
5064          */
5065         updateText : function(text)
5066         {
5067             if(!dlg.isVisible() && !opt.width){
5068                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5069                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5070             }
5071             msgEl.innerHTML = text || '&#160;';
5072       
5073             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5074             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5075             var w = Math.max(
5076                     Math.min(opt.width || cw , this.maxWidth), 
5077                     Math.max(opt.minWidth || this.minWidth, bwidth)
5078             );
5079             if(opt.prompt){
5080                 activeTextEl.setWidth(w);
5081             }
5082             if(dlg.isVisible()){
5083                 dlg.fixedcenter = false;
5084             }
5085             // to big, make it scroll. = But as usual stupid IE does not support
5086             // !important..
5087             
5088             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5089                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5090                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5091             } else {
5092                 bodyEl.dom.style.height = '';
5093                 bodyEl.dom.style.overflowY = '';
5094             }
5095             if (cw > w) {
5096                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5097             } else {
5098                 bodyEl.dom.style.overflowX = '';
5099             }
5100             
5101             dlg.setContentSize(w, bodyEl.getHeight());
5102             if(dlg.isVisible()){
5103                 dlg.fixedcenter = true;
5104             }
5105             return this;
5106         },
5107
5108         /**
5109          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5110          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5111          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5112          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         updateProgress : function(value, text){
5116             if(text){
5117                 this.updateText(text);
5118             }
5119             
5120             if (pp) { // weird bug on my firefox - for some reason this is not defined
5121                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5122                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5123             }
5124             return this;
5125         },        
5126
5127         /**
5128          * Returns true if the message box is currently displayed
5129          * @return {Boolean} True if the message box is visible, else false
5130          */
5131         isVisible : function(){
5132             return dlg && dlg.isVisible();  
5133         },
5134
5135         /**
5136          * Hides the message box if it is displayed
5137          */
5138         hide : function(){
5139             if(this.isVisible()){
5140                 dlg.hide();
5141             }  
5142         },
5143
5144         /**
5145          * Displays a new message box, or reinitializes an existing message box, based on the config options
5146          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5147          * The following config object properties are supported:
5148          * <pre>
5149 Property    Type             Description
5150 ----------  ---------------  ------------------------------------------------------------------------------------
5151 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5152                                    closes (defaults to undefined)
5153 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5154                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5155 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5156                                    progress and wait dialogs will ignore this property and always hide the
5157                                    close button as they can only be closed programmatically.
5158 cls               String           A custom CSS class to apply to the message box element
5159 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5160                                    displayed (defaults to 75)
5161 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5162                                    function will be btn (the name of the button that was clicked, if applicable,
5163                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5164                                    Progress and wait dialogs will ignore this option since they do not respond to
5165                                    user actions and can only be closed programmatically, so any required function
5166                                    should be called by the same code after it closes the dialog.
5167 icon              String           A CSS class that provides a background image to be used as an icon for
5168                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5169 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5170 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5171 modal             Boolean          False to allow user interaction with the page while the message box is
5172                                    displayed (defaults to true)
5173 msg               String           A string that will replace the existing message box body text (defaults
5174                                    to the XHTML-compliant non-breaking space character '&#160;')
5175 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5176 progress          Boolean          True to display a progress bar (defaults to false)
5177 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5178 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5179 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5180 title             String           The title text
5181 value             String           The string value to set into the active textbox element if displayed
5182 wait              Boolean          True to display a progress bar (defaults to false)
5183 width             Number           The width of the dialog in pixels
5184 </pre>
5185          *
5186          * Example usage:
5187          * <pre><code>
5188 Roo.Msg.show({
5189    title: 'Address',
5190    msg: 'Please enter your address:',
5191    width: 300,
5192    buttons: Roo.MessageBox.OKCANCEL,
5193    multiline: true,
5194    fn: saveAddress,
5195    animEl: 'addAddressBtn'
5196 });
5197 </code></pre>
5198          * @param {Object} config Configuration options
5199          * @return {Roo.MessageBox} This message box
5200          */
5201         show : function(options)
5202         {
5203             
5204             // this causes nightmares if you show one dialog after another
5205             // especially on callbacks..
5206              
5207             if(this.isVisible()){
5208                 
5209                 this.hide();
5210                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5211                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5212                 Roo.log("New Dialog Message:" +  options.msg )
5213                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5214                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5215                 
5216             }
5217             var d = this.getDialog();
5218             opt = options;
5219             d.setTitle(opt.title || "&#160;");
5220             d.closeEl.setDisplayed(opt.closable !== false);
5221             activeTextEl = textboxEl;
5222             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5223             if(opt.prompt){
5224                 if(opt.multiline){
5225                     textboxEl.hide();
5226                     textareaEl.show();
5227                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5228                         opt.multiline : this.defaultTextHeight);
5229                     activeTextEl = textareaEl;
5230                 }else{
5231                     textboxEl.show();
5232                     textareaEl.hide();
5233                 }
5234             }else{
5235                 textboxEl.hide();
5236                 textareaEl.hide();
5237             }
5238             progressEl.setDisplayed(opt.progress === true);
5239             if (opt.progress) {
5240                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5241             }
5242             this.updateProgress(0);
5243             activeTextEl.dom.value = opt.value || "";
5244             if(opt.prompt){
5245                 dlg.setDefaultButton(activeTextEl);
5246             }else{
5247                 var bs = opt.buttons;
5248                 var db = null;
5249                 if(bs && bs.ok){
5250                     db = buttons["ok"];
5251                 }else if(bs && bs.yes){
5252                     db = buttons["yes"];
5253                 }
5254                 dlg.setDefaultButton(db);
5255             }
5256             bwidth = updateButtons(opt.buttons);
5257             this.updateText(opt.msg);
5258             if(opt.cls){
5259                 d.el.addClass(opt.cls);
5260             }
5261             d.proxyDrag = opt.proxyDrag === true;
5262             d.modal = opt.modal !== false;
5263             d.mask = opt.modal !== false ? mask : false;
5264             if(!d.isVisible()){
5265                 // force it to the end of the z-index stack so it gets a cursor in FF
5266                 document.body.appendChild(dlg.el.dom);
5267                 d.animateTarget = null;
5268                 d.show(options.animEl);
5269             }
5270             return this;
5271         },
5272
5273         /**
5274          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5275          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5276          * and closing the message box when the process is complete.
5277          * @param {String} title The title bar text
5278          * @param {String} msg The message box body text
5279          * @return {Roo.MessageBox} This message box
5280          */
5281         progress : function(title, msg){
5282             this.show({
5283                 title : title,
5284                 msg : msg,
5285                 buttons: false,
5286                 progress:true,
5287                 closable:false,
5288                 minWidth: this.minProgressWidth,
5289                 modal : true
5290             });
5291             return this;
5292         },
5293
5294         /**
5295          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5296          * If a callback function is passed it will be called after the user clicks the button, and the
5297          * id of the button that was clicked will be passed as the only parameter to the callback
5298          * (could also be the top-right close button).
5299          * @param {String} title The title bar text
5300          * @param {String} msg The message box body text
5301          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302          * @param {Object} scope (optional) The scope of the callback function
5303          * @return {Roo.MessageBox} This message box
5304          */
5305         alert : function(title, msg, fn, scope)
5306         {
5307             this.show({
5308                 title : title,
5309                 msg : msg,
5310                 buttons: this.OK,
5311                 fn: fn,
5312                 closable : false,
5313                 scope : scope,
5314                 modal : true
5315             });
5316             return this;
5317         },
5318
5319         /**
5320          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5321          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5322          * You are responsible for closing the message box when the process is complete.
5323          * @param {String} msg The message box body text
5324          * @param {String} title (optional) The title bar text
5325          * @return {Roo.MessageBox} This message box
5326          */
5327         wait : function(msg, title){
5328             this.show({
5329                 title : title,
5330                 msg : msg,
5331                 buttons: false,
5332                 closable:false,
5333                 progress:true,
5334                 modal:true,
5335                 width:300,
5336                 wait:true
5337             });
5338             waitTimer = Roo.TaskMgr.start({
5339                 run: function(i){
5340                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5341                 },
5342                 interval: 1000
5343             });
5344             return this;
5345         },
5346
5347         /**
5348          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5349          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5350          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5351          * @param {String} title The title bar text
5352          * @param {String} msg The message box body text
5353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354          * @param {Object} scope (optional) The scope of the callback function
5355          * @return {Roo.MessageBox} This message box
5356          */
5357         confirm : function(title, msg, fn, scope){
5358             this.show({
5359                 title : title,
5360                 msg : msg,
5361                 buttons: this.YESNO,
5362                 fn: fn,
5363                 scope : scope,
5364                 modal : true
5365             });
5366             return this;
5367         },
5368
5369         /**
5370          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5371          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5372          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5373          * (could also be the top-right close button) and the text that was entered will be passed as the two
5374          * parameters to the callback.
5375          * @param {String} title The title bar text
5376          * @param {String} msg The message box body text
5377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378          * @param {Object} scope (optional) The scope of the callback function
5379          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5380          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         prompt : function(title, msg, fn, scope, multiline){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: this.OKCANCEL,
5388                 fn: fn,
5389                 minWidth:250,
5390                 scope : scope,
5391                 prompt:true,
5392                 multiline: multiline,
5393                 modal : true
5394             });
5395             return this;
5396         },
5397
5398         /**
5399          * Button config that displays a single OK button
5400          * @type Object
5401          */
5402         OK : {ok:true},
5403         /**
5404          * Button config that displays Yes and No buttons
5405          * @type Object
5406          */
5407         YESNO : {yes:true, no:true},
5408         /**
5409          * Button config that displays OK and Cancel buttons
5410          * @type Object
5411          */
5412         OKCANCEL : {ok:true, cancel:true},
5413         /**
5414          * Button config that displays Yes, No and Cancel buttons
5415          * @type Object
5416          */
5417         YESNOCANCEL : {yes:true, no:true, cancel:true},
5418
5419         /**
5420          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5421          * @type Number
5422          */
5423         defaultTextHeight : 75,
5424         /**
5425          * The maximum width in pixels of the message box (defaults to 600)
5426          * @type Number
5427          */
5428         maxWidth : 600,
5429         /**
5430          * The minimum width in pixels of the message box (defaults to 100)
5431          * @type Number
5432          */
5433         minWidth : 100,
5434         /**
5435          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5436          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5437          * @type Number
5438          */
5439         minProgressWidth : 250,
5440         /**
5441          * An object containing the default button text strings that can be overriden for localized language support.
5442          * Supported properties are: ok, cancel, yes and no.
5443          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5444          * @type Object
5445          */
5446         buttonText : {
5447             ok : "OK",
5448             cancel : "Cancel",
5449             yes : "Yes",
5450             no : "No"
5451         }
5452     };
5453 }();
5454
5455 /**
5456  * Shorthand for {@link Roo.MessageBox}
5457  */
5458 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5459 Roo.Msg = Roo.Msg || Roo.MessageBox;
5460 /*
5461  * - LGPL
5462  *
5463  * navbar
5464  * 
5465  */
5466
5467 /**
5468  * @class Roo.bootstrap.Navbar
5469  * @extends Roo.bootstrap.Component
5470  * Bootstrap Navbar class
5471
5472  * @constructor
5473  * Create a new Navbar
5474  * @param {Object} config The config object
5475  */
5476
5477
5478 Roo.bootstrap.Navbar = function(config){
5479     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5480     this.addEvents({
5481         // raw events
5482         /**
5483          * @event beforetoggle
5484          * Fire before toggle the menu
5485          * @param {Roo.EventObject} e
5486          */
5487         "beforetoggle" : true
5488     });
5489 };
5490
5491 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5492     
5493     
5494    
5495     // private
5496     navItems : false,
5497     loadMask : false,
5498     
5499     
5500     getAutoCreate : function(){
5501         
5502         
5503         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5504         
5505     },
5506     
5507     initEvents :function ()
5508     {
5509         //Roo.log(this.el.select('.navbar-toggle',true));
5510         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5511         
5512         var mark = {
5513             tag: "div",
5514             cls:"x-dlg-mask"
5515         };
5516         
5517         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5518         
5519         var size = this.el.getSize();
5520         this.maskEl.setSize(size.width, size.height);
5521         this.maskEl.enableDisplayMode("block");
5522         this.maskEl.hide();
5523         
5524         if(this.loadMask){
5525             this.maskEl.show();
5526         }
5527     },
5528     
5529     
5530     getChildContainer : function()
5531     {
5532         if (this.el && this.el.select('.collapse').getCount()) {
5533             return this.el.select('.collapse',true).first();
5534         }
5535         
5536         return this.el;
5537     },
5538     
5539     mask : function()
5540     {
5541         this.maskEl.show();
5542     },
5543     
5544     unmask : function()
5545     {
5546         this.maskEl.hide();
5547     },
5548     onToggle : function()
5549     {
5550         
5551         if(this.fireEvent('beforetoggle', this) === false){
5552             return;
5553         }
5554         var ce = this.el.select('.navbar-collapse',true).first();
5555       
5556         if (!ce.hasClass('show')) {
5557            this.expand();
5558         } else {
5559             this.collapse();
5560         }
5561         
5562         
5563     
5564     },
5565     /**
5566      * Expand the navbar pulldown 
5567      */
5568     expand : function ()
5569     {
5570        
5571         var ce = this.el.select('.navbar-collapse',true).first();
5572         if (ce.hasClass('collapsing')) {
5573             return;
5574         }
5575         ce.dom.style.height = '';
5576                // show it...
5577         ce.addClass('in'); // old...
5578         ce.removeClass('collapse');
5579         ce.addClass('show');
5580         var h = ce.getHeight();
5581         Roo.log(h);
5582         ce.removeClass('show');
5583         // at this point we should be able to see it..
5584         ce.addClass('collapsing');
5585         
5586         ce.setHeight(0); // resize it ...
5587         ce.on('transitionend', function() {
5588             //Roo.log('done transition');
5589             ce.removeClass('collapsing');
5590             ce.addClass('show');
5591             ce.removeClass('collapse');
5592
5593             ce.dom.style.height = '';
5594         }, this, { single: true} );
5595         ce.setHeight(h);
5596         ce.dom.scrollTop = 0;
5597     },
5598     /**
5599      * Collapse the navbar pulldown 
5600      */
5601     collapse : function()
5602     {
5603          var ce = this.el.select('.navbar-collapse',true).first();
5604        
5605         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5606             // it's collapsed or collapsing..
5607             return;
5608         }
5609         ce.removeClass('in'); // old...
5610         ce.setHeight(ce.getHeight());
5611         ce.removeClass('show');
5612         ce.addClass('collapsing');
5613         
5614         ce.on('transitionend', function() {
5615             ce.dom.style.height = '';
5616             ce.removeClass('collapsing');
5617             ce.addClass('collapse');
5618         }, this, { single: true} );
5619         ce.setHeight(0);
5620     }
5621     
5622     
5623     
5624 });
5625
5626
5627
5628  
5629
5630  /*
5631  * - LGPL
5632  *
5633  * navbar
5634  * 
5635  */
5636
5637 /**
5638  * @class Roo.bootstrap.NavSimplebar
5639  * @extends Roo.bootstrap.Navbar
5640  * Bootstrap Sidebar class
5641  *
5642  * @cfg {Boolean} inverse is inverted color
5643  * 
5644  * @cfg {String} type (nav | pills | tabs)
5645  * @cfg {Boolean} arrangement stacked | justified
5646  * @cfg {String} align (left | right) alignment
5647  * 
5648  * @cfg {Boolean} main (true|false) main nav bar? default false
5649  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5650  * 
5651  * @cfg {String} tag (header|footer|nav|div) default is nav 
5652
5653  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5654  * 
5655  * 
5656  * @constructor
5657  * Create a new Sidebar
5658  * @param {Object} config The config object
5659  */
5660
5661
5662 Roo.bootstrap.NavSimplebar = function(config){
5663     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5664 };
5665
5666 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5667     
5668     inverse: false,
5669     
5670     type: false,
5671     arrangement: '',
5672     align : false,
5673     
5674     weight : 'light',
5675     
5676     main : false,
5677     
5678     
5679     tag : false,
5680     
5681     
5682     getAutoCreate : function(){
5683         
5684         
5685         var cfg = {
5686             tag : this.tag || 'div',
5687             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5688         };
5689         if (['light','white'].indexOf(this.weight) > -1) {
5690             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5691         }
5692         cfg.cls += ' bg-' + this.weight;
5693         
5694         if (this.inverse) {
5695             cfg.cls += ' navbar-inverse';
5696             
5697         }
5698         
5699         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5700         
5701         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5702             return cfg;
5703         }
5704         
5705         
5706     
5707         
5708         cfg.cn = [
5709             {
5710                 cls: 'nav nav-' + this.xtype,
5711                 tag : 'ul'
5712             }
5713         ];
5714         
5715          
5716         this.type = this.type || 'nav';
5717         if (['tabs','pills'].indexOf(this.type) != -1) {
5718             cfg.cn[0].cls += ' nav-' + this.type
5719         
5720         
5721         } else {
5722             if (this.type!=='nav') {
5723                 Roo.log('nav type must be nav/tabs/pills')
5724             }
5725             cfg.cn[0].cls += ' navbar-nav'
5726         }
5727         
5728         
5729         
5730         
5731         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5732             cfg.cn[0].cls += ' nav-' + this.arrangement;
5733         }
5734         
5735         
5736         if (this.align === 'right') {
5737             cfg.cn[0].cls += ' navbar-right';
5738         }
5739         
5740         
5741         
5742         
5743         return cfg;
5744     
5745         
5746     }
5747     
5748     
5749     
5750 });
5751
5752
5753
5754  
5755
5756  
5757        /*
5758  * - LGPL
5759  *
5760  * navbar
5761  * navbar-fixed-top
5762  * navbar-expand-md  fixed-top 
5763  */
5764
5765 /**
5766  * @class Roo.bootstrap.NavHeaderbar
5767  * @extends Roo.bootstrap.NavSimplebar
5768  * Bootstrap Sidebar class
5769  *
5770  * @cfg {String} brand what is brand
5771  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5772  * @cfg {String} brand_href href of the brand
5773  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5774  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5775  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5776  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5777  * 
5778  * @constructor
5779  * Create a new Sidebar
5780  * @param {Object} config The config object
5781  */
5782
5783
5784 Roo.bootstrap.NavHeaderbar = function(config){
5785     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5786       
5787 };
5788
5789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5790     
5791     position: '',
5792     brand: '',
5793     brand_href: false,
5794     srButton : true,
5795     autohide : false,
5796     desktopCenter : false,
5797    
5798     
5799     getAutoCreate : function(){
5800         
5801         var   cfg = {
5802             tag: this.nav || 'nav',
5803             cls: 'navbar navbar-expand-md',
5804             role: 'navigation',
5805             cn: []
5806         };
5807         
5808         var cn = cfg.cn;
5809         if (this.desktopCenter) {
5810             cn.push({cls : 'container', cn : []});
5811             cn = cn[0].cn;
5812         }
5813         
5814         if(this.srButton){
5815             var btn = {
5816                 tag: 'button',
5817                 type: 'button',
5818                 cls: 'navbar-toggle navbar-toggler',
5819                 'data-toggle': 'collapse',
5820                 cn: [
5821                     {
5822                         tag: 'span',
5823                         cls: 'sr-only',
5824                         html: 'Toggle navigation'
5825                     },
5826                     {
5827                         tag: 'span',
5828                         cls: 'icon-bar navbar-toggler-icon'
5829                     },
5830                     {
5831                         tag: 'span',
5832                         cls: 'icon-bar'
5833                     },
5834                     {
5835                         tag: 'span',
5836                         cls: 'icon-bar'
5837                     }
5838                 ]
5839             };
5840             
5841             cn.push( Roo.bootstrap.version == 4 ? btn : {
5842                 tag: 'div',
5843                 cls: 'navbar-header',
5844                 cn: [
5845                     btn
5846                 ]
5847             });
5848         }
5849         
5850         cn.push({
5851             tag: 'div',
5852             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5853             cn : []
5854         });
5855         
5856         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5857         
5858         if (['light','white'].indexOf(this.weight) > -1) {
5859             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5860         }
5861         cfg.cls += ' bg-' + this.weight;
5862         
5863         
5864         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5865             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5866             
5867             // tag can override this..
5868             
5869             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5870         }
5871         
5872         if (this.brand !== '') {
5873             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5874             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5875                 tag: 'a',
5876                 href: this.brand_href ? this.brand_href : '#',
5877                 cls: 'navbar-brand',
5878                 cn: [
5879                 this.brand
5880                 ]
5881             });
5882         }
5883         
5884         if(this.main){
5885             cfg.cls += ' main-nav';
5886         }
5887         
5888         
5889         return cfg;
5890
5891         
5892     },
5893     getHeaderChildContainer : function()
5894     {
5895         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5896             return this.el.select('.navbar-header',true).first();
5897         }
5898         
5899         return this.getChildContainer();
5900     },
5901     
5902     getChildContainer : function()
5903     {
5904          
5905         return this.el.select('.roo-navbar-collapse',true).first();
5906          
5907         
5908     },
5909     
5910     initEvents : function()
5911     {
5912         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5913         
5914         if (this.autohide) {
5915             
5916             var prevScroll = 0;
5917             var ft = this.el;
5918             
5919             Roo.get(document).on('scroll',function(e) {
5920                 var ns = Roo.get(document).getScroll().top;
5921                 var os = prevScroll;
5922                 prevScroll = ns;
5923                 
5924                 if(ns > os){
5925                     ft.removeClass('slideDown');
5926                     ft.addClass('slideUp');
5927                     return;
5928                 }
5929                 ft.removeClass('slideUp');
5930                 ft.addClass('slideDown');
5931                  
5932               
5933           },this);
5934         }
5935     }    
5936     
5937 });
5938
5939
5940
5941  
5942
5943  /*
5944  * - LGPL
5945  *
5946  * navbar
5947  * 
5948  */
5949
5950 /**
5951  * @class Roo.bootstrap.NavSidebar
5952  * @extends Roo.bootstrap.Navbar
5953  * Bootstrap Sidebar class
5954  * 
5955  * @constructor
5956  * Create a new Sidebar
5957  * @param {Object} config The config object
5958  */
5959
5960
5961 Roo.bootstrap.NavSidebar = function(config){
5962     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5963 };
5964
5965 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5966     
5967     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5968     
5969     getAutoCreate : function(){
5970         
5971         
5972         return  {
5973             tag: 'div',
5974             cls: 'sidebar sidebar-nav'
5975         };
5976     
5977         
5978     }
5979     
5980     
5981     
5982 });
5983
5984
5985
5986  
5987
5988  /*
5989  * - LGPL
5990  *
5991  * nav group
5992  * 
5993  */
5994
5995 /**
5996  * @class Roo.bootstrap.NavGroup
5997  * @extends Roo.bootstrap.Component
5998  * Bootstrap NavGroup class
5999  * @cfg {String} align (left|right)
6000  * @cfg {Boolean} inverse
6001  * @cfg {String} type (nav|pills|tab) default nav
6002  * @cfg {String} navId - reference Id for navbar.
6003  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6004  * 
6005  * @constructor
6006  * Create a new nav group
6007  * @param {Object} config The config object
6008  */
6009
6010 Roo.bootstrap.NavGroup = function(config){
6011     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6012     this.navItems = [];
6013    
6014     Roo.bootstrap.NavGroup.register(this);
6015      this.addEvents({
6016         /**
6017              * @event changed
6018              * Fires when the active item changes
6019              * @param {Roo.bootstrap.NavGroup} this
6020              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6021              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6022          */
6023         'changed': true
6024      });
6025     
6026 };
6027
6028 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6029     
6030     align: '',
6031     inverse: false,
6032     form: false,
6033     type: 'nav',
6034     navId : '',
6035     // private
6036     pilltype : true,
6037     
6038     navItems : false, 
6039     
6040     getAutoCreate : function()
6041     {
6042         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6043         
6044         cfg = {
6045             tag : 'ul',
6046             cls: 'nav' 
6047         };
6048         if (Roo.bootstrap.version == 4) {
6049             if (['tabs','pills'].indexOf(this.type) != -1) {
6050                 cfg.cls += ' nav-' + this.type; 
6051             } else {
6052                 // trying to remove so header bar can right align top?
6053                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6054                     // do not use on header bar... 
6055                     cfg.cls += ' navbar-nav';
6056                 }
6057             }
6058             
6059         } else {
6060             if (['tabs','pills'].indexOf(this.type) != -1) {
6061                 cfg.cls += ' nav-' + this.type
6062             } else {
6063                 if (this.type !== 'nav') {
6064                     Roo.log('nav type must be nav/tabs/pills')
6065                 }
6066                 cfg.cls += ' navbar-nav'
6067             }
6068         }
6069         
6070         if (this.parent() && this.parent().sidebar) {
6071             cfg = {
6072                 tag: 'ul',
6073                 cls: 'dashboard-menu sidebar-menu'
6074             };
6075             
6076             return cfg;
6077         }
6078         
6079         if (this.form === true) {
6080             cfg = {
6081                 tag: 'form',
6082                 cls: 'navbar-form form-inline'
6083             };
6084             //nav navbar-right ml-md-auto
6085             if (this.align === 'right') {
6086                 cfg.cls += ' navbar-right ml-md-auto';
6087             } else {
6088                 cfg.cls += ' navbar-left';
6089             }
6090         }
6091         
6092         if (this.align === 'right') {
6093             cfg.cls += ' navbar-right ml-md-auto';
6094         } else {
6095             cfg.cls += ' mr-auto';
6096         }
6097         
6098         if (this.inverse) {
6099             cfg.cls += ' navbar-inverse';
6100             
6101         }
6102         
6103         
6104         return cfg;
6105     },
6106     /**
6107     * sets the active Navigation item
6108     * @param {Roo.bootstrap.NavItem} the new current navitem
6109     */
6110     setActiveItem : function(item)
6111     {
6112         var prev = false;
6113         Roo.each(this.navItems, function(v){
6114             if (v == item) {
6115                 return ;
6116             }
6117             if (v.isActive()) {
6118                 v.setActive(false, true);
6119                 prev = v;
6120                 
6121             }
6122             
6123         });
6124
6125         item.setActive(true, true);
6126         this.fireEvent('changed', this, item, prev);
6127         
6128         
6129     },
6130     /**
6131     * gets the active Navigation item
6132     * @return {Roo.bootstrap.NavItem} the current navitem
6133     */
6134     getActive : function()
6135     {
6136         
6137         var prev = false;
6138         Roo.each(this.navItems, function(v){
6139             
6140             if (v.isActive()) {
6141                 prev = v;
6142                 
6143             }
6144             
6145         });
6146         return prev;
6147     },
6148     
6149     indexOfNav : function()
6150     {
6151         
6152         var prev = false;
6153         Roo.each(this.navItems, function(v,i){
6154             
6155             if (v.isActive()) {
6156                 prev = i;
6157                 
6158             }
6159             
6160         });
6161         return prev;
6162     },
6163     /**
6164     * adds a Navigation item
6165     * @param {Roo.bootstrap.NavItem} the navitem to add
6166     */
6167     addItem : function(cfg)
6168     {
6169         if (this.form && Roo.bootstrap.version == 4) {
6170             cfg.tag = 'div';
6171         }
6172         var cn = new Roo.bootstrap.NavItem(cfg);
6173         this.register(cn);
6174         cn.parentId = this.id;
6175         cn.onRender(this.el, null);
6176         return cn;
6177     },
6178     /**
6179     * register a Navigation item
6180     * @param {Roo.bootstrap.NavItem} the navitem to add
6181     */
6182     register : function(item)
6183     {
6184         this.navItems.push( item);
6185         item.navId = this.navId;
6186     
6187     },
6188     
6189     /**
6190     * clear all the Navigation item
6191     */
6192    
6193     clearAll : function()
6194     {
6195         this.navItems = [];
6196         this.el.dom.innerHTML = '';
6197     },
6198     
6199     getNavItem: function(tabId)
6200     {
6201         var ret = false;
6202         Roo.each(this.navItems, function(e) {
6203             if (e.tabId == tabId) {
6204                ret =  e;
6205                return false;
6206             }
6207             return true;
6208             
6209         });
6210         return ret;
6211     },
6212     
6213     setActiveNext : function()
6214     {
6215         var i = this.indexOfNav(this.getActive());
6216         if (i > this.navItems.length) {
6217             return;
6218         }
6219         this.setActiveItem(this.navItems[i+1]);
6220     },
6221     setActivePrev : function()
6222     {
6223         var i = this.indexOfNav(this.getActive());
6224         if (i  < 1) {
6225             return;
6226         }
6227         this.setActiveItem(this.navItems[i-1]);
6228     },
6229     clearWasActive : function(except) {
6230         Roo.each(this.navItems, function(e) {
6231             if (e.tabId != except.tabId && e.was_active) {
6232                e.was_active = false;
6233                return false;
6234             }
6235             return true;
6236             
6237         });
6238     },
6239     getWasActive : function ()
6240     {
6241         var r = false;
6242         Roo.each(this.navItems, function(e) {
6243             if (e.was_active) {
6244                r = e;
6245                return false;
6246             }
6247             return true;
6248             
6249         });
6250         return r;
6251     }
6252     
6253     
6254 });
6255
6256  
6257 Roo.apply(Roo.bootstrap.NavGroup, {
6258     
6259     groups: {},
6260      /**
6261     * register a Navigation Group
6262     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6263     */
6264     register : function(navgrp)
6265     {
6266         this.groups[navgrp.navId] = navgrp;
6267         
6268     },
6269     /**
6270     * fetch a Navigation Group based on the navigation ID
6271     * @param {string} the navgroup to add
6272     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6273     */
6274     get: function(navId) {
6275         if (typeof(this.groups[navId]) == 'undefined') {
6276             return false;
6277             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6278         }
6279         return this.groups[navId] ;
6280     }
6281     
6282     
6283     
6284 });
6285
6286  /*
6287  * - LGPL
6288  *
6289  * row
6290  * 
6291  */
6292
6293 /**
6294  * @class Roo.bootstrap.NavItem
6295  * @extends Roo.bootstrap.Component
6296  * Bootstrap Navbar.NavItem class
6297  * @cfg {String} href  link to
6298  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6299  * @cfg {Boolean} button_outline show and outlined button
6300  * @cfg {String} html content of button
6301  * @cfg {String} badge text inside badge
6302  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6303  * @cfg {String} glyphicon DEPRICATED - use fa
6304  * @cfg {String} icon DEPRICATED - use fa
6305  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6306  * @cfg {Boolean} active Is item active
6307  * @cfg {Boolean} disabled Is item disabled
6308  * @cfg {String} linkcls  Link Class
6309  * @cfg {Boolean} preventDefault (true | false) default false
6310  * @cfg {String} tabId the tab that this item activates.
6311  * @cfg {String} tagtype (a|span) render as a href or span?
6312  * @cfg {Boolean} animateRef (true|false) link to element default false  
6313   
6314  * @constructor
6315  * Create a new Navbar Item
6316  * @param {Object} config The config object
6317  */
6318 Roo.bootstrap.NavItem = function(config){
6319     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6320     this.addEvents({
6321         // raw events
6322         /**
6323          * @event click
6324          * The raw click event for the entire grid.
6325          * @param {Roo.EventObject} e
6326          */
6327         "click" : true,
6328          /**
6329             * @event changed
6330             * Fires when the active item active state changes
6331             * @param {Roo.bootstrap.NavItem} this
6332             * @param {boolean} state the new state
6333              
6334          */
6335         'changed': true,
6336         /**
6337             * @event scrollto
6338             * Fires when scroll to element
6339             * @param {Roo.bootstrap.NavItem} this
6340             * @param {Object} options
6341             * @param {Roo.EventObject} e
6342              
6343          */
6344         'scrollto': true
6345     });
6346    
6347 };
6348
6349 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6350     
6351     href: false,
6352     html: '',
6353     badge: '',
6354     icon: false,
6355     fa : false,
6356     glyphicon: false,
6357     active: false,
6358     preventDefault : false,
6359     tabId : false,
6360     tagtype : 'a',
6361     tag: 'li',
6362     disabled : false,
6363     animateRef : false,
6364     was_active : false,
6365     button_weight : '',
6366     button_outline : false,
6367     linkcls : '',
6368     navLink: false,
6369     
6370     getAutoCreate : function(){
6371          
6372         var cfg = {
6373             tag: this.tag,
6374             cls: 'nav-item'
6375         };
6376         
6377         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6378         
6379         if (this.active) {
6380             cfg.cls +=  ' active' ;
6381         }
6382         if (this.disabled) {
6383             cfg.cls += ' disabled';
6384         }
6385         
6386         // BS4 only?
6387         if (this.button_weight.length) {
6388             cfg.tag = this.href ? 'a' : 'button';
6389             cfg.html = this.html || '';
6390             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6391             if (this.href) {
6392                 cfg.href = this.href;
6393             }
6394             if (this.fa) {
6395                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6396             } else {
6397                 cfg.cls += " nav-html";
6398             }
6399             
6400             // menu .. should add dropdown-menu class - so no need for carat..
6401             
6402             if (this.badge !== '') {
6403                  
6404                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6405             }
6406             return cfg;
6407         }
6408         
6409         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6410             cfg.cn = [
6411                 {
6412                     tag: this.tagtype,
6413                     href : this.href || "#",
6414                     html: this.html || '',
6415                     cls : ''
6416                 }
6417             ];
6418             if (this.tagtype == 'a') {
6419                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6420         
6421             }
6422             if (this.icon) {
6423                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6424             } else  if (this.fa) {
6425                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6426             } else if(this.glyphicon) {
6427                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6428             } else {
6429                 cfg.cn[0].cls += " nav-html";
6430             }
6431             
6432             if (this.menu) {
6433                 cfg.cn[0].html += " <span class='caret'></span>";
6434              
6435             }
6436             
6437             if (this.badge !== '') {
6438                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6439             }
6440         }
6441         
6442         
6443         
6444         return cfg;
6445     },
6446     onRender : function(ct, position)
6447     {
6448        // Roo.log("Call onRender: " + this.xtype);
6449         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6450             this.tag = 'div';
6451         }
6452         
6453         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6454         this.navLink = this.el.select('.nav-link',true).first();
6455         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6456         return ret;
6457     },
6458       
6459     
6460     initEvents: function() 
6461     {
6462         if (typeof (this.menu) != 'undefined') {
6463             this.menu.parentType = this.xtype;
6464             this.menu.triggerEl = this.el;
6465             this.menu = this.addxtype(Roo.apply({}, this.menu));
6466         }
6467         
6468         this.el.on('click', this.onClick, this);
6469         
6470         //if(this.tagtype == 'span'){
6471         //    this.el.select('span',true).on('click', this.onClick, this);
6472         //}
6473        
6474         // at this point parent should be available..
6475         this.parent().register(this);
6476     },
6477     
6478     onClick : function(e)
6479     {
6480         if (e.getTarget('.dropdown-menu-item')) {
6481             // did you click on a menu itemm.... - then don't trigger onclick..
6482             return;
6483         }
6484         
6485         if(
6486                 this.preventDefault || 
6487                 this.href == '#' 
6488         ){
6489             Roo.log("NavItem - prevent Default?");
6490             e.preventDefault();
6491         }
6492         
6493         if (this.disabled) {
6494             return;
6495         }
6496         
6497         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6498         if (tg && tg.transition) {
6499             Roo.log("waiting for the transitionend");
6500             return;
6501         }
6502         
6503         
6504         
6505         //Roo.log("fire event clicked");
6506         if(this.fireEvent('click', this, e) === false){
6507             return;
6508         };
6509         
6510         if(this.tagtype == 'span'){
6511             return;
6512         }
6513         
6514         //Roo.log(this.href);
6515         var ael = this.el.select('a',true).first();
6516         //Roo.log(ael);
6517         
6518         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6519             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6520             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6521                 return; // ignore... - it's a 'hash' to another page.
6522             }
6523             Roo.log("NavItem - prevent Default?");
6524             e.preventDefault();
6525             this.scrollToElement(e);
6526         }
6527         
6528         
6529         var p =  this.parent();
6530    
6531         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6532             if (typeof(p.setActiveItem) !== 'undefined') {
6533                 p.setActiveItem(this);
6534             }
6535         }
6536         
6537         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6538         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6539             // remove the collapsed menu expand...
6540             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6541         }
6542     },
6543     
6544     isActive: function () {
6545         return this.active
6546     },
6547     setActive : function(state, fire, is_was_active)
6548     {
6549         if (this.active && !state && this.navId) {
6550             this.was_active = true;
6551             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6552             if (nv) {
6553                 nv.clearWasActive(this);
6554             }
6555             
6556         }
6557         this.active = state;
6558         
6559         if (!state ) {
6560             this.el.removeClass('active');
6561             this.navLink ? this.navLink.removeClass('active') : false;
6562         } else if (!this.el.hasClass('active')) {
6563             
6564             this.el.addClass('active');
6565             if (Roo.bootstrap.version == 4 && this.navLink ) {
6566                 this.navLink.addClass('active');
6567             }
6568             
6569         }
6570         if (fire) {
6571             this.fireEvent('changed', this, state);
6572         }
6573         
6574         // show a panel if it's registered and related..
6575         
6576         if (!this.navId || !this.tabId || !state || is_was_active) {
6577             return;
6578         }
6579         
6580         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6581         if (!tg) {
6582             return;
6583         }
6584         var pan = tg.getPanelByName(this.tabId);
6585         if (!pan) {
6586             return;
6587         }
6588         // if we can not flip to new panel - go back to old nav highlight..
6589         if (false == tg.showPanel(pan)) {
6590             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6591             if (nv) {
6592                 var onav = nv.getWasActive();
6593                 if (onav) {
6594                     onav.setActive(true, false, true);
6595                 }
6596             }
6597             
6598         }
6599         
6600         
6601         
6602     },
6603      // this should not be here...
6604     setDisabled : function(state)
6605     {
6606         this.disabled = state;
6607         if (!state ) {
6608             this.el.removeClass('disabled');
6609         } else if (!this.el.hasClass('disabled')) {
6610             this.el.addClass('disabled');
6611         }
6612         
6613     },
6614     
6615     /**
6616      * Fetch the element to display the tooltip on.
6617      * @return {Roo.Element} defaults to this.el
6618      */
6619     tooltipEl : function()
6620     {
6621         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6622     },
6623     
6624     scrollToElement : function(e)
6625     {
6626         var c = document.body;
6627         
6628         /*
6629          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6630          */
6631         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6632             c = document.documentElement;
6633         }
6634         
6635         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6636         
6637         if(!target){
6638             return;
6639         }
6640
6641         var o = target.calcOffsetsTo(c);
6642         
6643         var options = {
6644             target : target,
6645             value : o[1]
6646         };
6647         
6648         this.fireEvent('scrollto', this, options, e);
6649         
6650         Roo.get(c).scrollTo('top', options.value, true);
6651         
6652         return;
6653     },
6654     /**
6655      * Set the HTML (text content) of the item
6656      * @param {string} html  content for the nav item
6657      */
6658     setHtml : function(html)
6659     {
6660         this.html = html;
6661         this.htmlEl.dom.innerHTML = html;
6662         
6663     } 
6664 });
6665  
6666
6667  /*
6668  * - LGPL
6669  *
6670  * sidebar item
6671  *
6672  *  li
6673  *    <span> icon </span>
6674  *    <span> text </span>
6675  *    <span>badge </span>
6676  */
6677
6678 /**
6679  * @class Roo.bootstrap.NavSidebarItem
6680  * @extends Roo.bootstrap.NavItem
6681  * Bootstrap Navbar.NavSidebarItem class
6682  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6683  * {Boolean} open is the menu open
6684  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6685  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6686  * {String} buttonSize (sm|md|lg)the extra classes for the button
6687  * {Boolean} showArrow show arrow next to the text (default true)
6688  * @constructor
6689  * Create a new Navbar Button
6690  * @param {Object} config The config object
6691  */
6692 Roo.bootstrap.NavSidebarItem = function(config){
6693     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6694     this.addEvents({
6695         // raw events
6696         /**
6697          * @event click
6698          * The raw click event for the entire grid.
6699          * @param {Roo.EventObject} e
6700          */
6701         "click" : true,
6702          /**
6703             * @event changed
6704             * Fires when the active item active state changes
6705             * @param {Roo.bootstrap.NavSidebarItem} this
6706             * @param {boolean} state the new state
6707              
6708          */
6709         'changed': true
6710     });
6711    
6712 };
6713
6714 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6715     
6716     badgeWeight : 'default',
6717     
6718     open: false,
6719     
6720     buttonView : false,
6721     
6722     buttonWeight : 'default',
6723     
6724     buttonSize : 'md',
6725     
6726     showArrow : true,
6727     
6728     getAutoCreate : function(){
6729         
6730         
6731         var a = {
6732                 tag: 'a',
6733                 href : this.href || '#',
6734                 cls: '',
6735                 html : '',
6736                 cn : []
6737         };
6738         
6739         if(this.buttonView){
6740             a = {
6741                 tag: 'button',
6742                 href : this.href || '#',
6743                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6744                 html : this.html,
6745                 cn : []
6746             };
6747         }
6748         
6749         var cfg = {
6750             tag: 'li',
6751             cls: '',
6752             cn: [ a ]
6753         };
6754         
6755         if (this.active) {
6756             cfg.cls += ' active';
6757         }
6758         
6759         if (this.disabled) {
6760             cfg.cls += ' disabled';
6761         }
6762         if (this.open) {
6763             cfg.cls += ' open x-open';
6764         }
6765         // left icon..
6766         if (this.glyphicon || this.icon) {
6767             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6768             a.cn.push({ tag : 'i', cls : c }) ;
6769         }
6770         
6771         if(!this.buttonView){
6772             var span = {
6773                 tag: 'span',
6774                 html : this.html || ''
6775             };
6776
6777             a.cn.push(span);
6778             
6779         }
6780         
6781         if (this.badge !== '') {
6782             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6783         }
6784         
6785         if (this.menu) {
6786             
6787             if(this.showArrow){
6788                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6789             }
6790             
6791             a.cls += ' dropdown-toggle treeview' ;
6792         }
6793         
6794         return cfg;
6795     },
6796     
6797     initEvents : function()
6798     { 
6799         if (typeof (this.menu) != 'undefined') {
6800             this.menu.parentType = this.xtype;
6801             this.menu.triggerEl = this.el;
6802             this.menu = this.addxtype(Roo.apply({}, this.menu));
6803         }
6804         
6805         this.el.on('click', this.onClick, this);
6806         
6807         if(this.badge !== ''){
6808             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6809         }
6810         
6811     },
6812     
6813     onClick : function(e)
6814     {
6815         if(this.disabled){
6816             e.preventDefault();
6817             return;
6818         }
6819         
6820         if(this.preventDefault){
6821             e.preventDefault();
6822         }
6823         
6824         this.fireEvent('click', this, e);
6825     },
6826     
6827     disable : function()
6828     {
6829         this.setDisabled(true);
6830     },
6831     
6832     enable : function()
6833     {
6834         this.setDisabled(false);
6835     },
6836     
6837     setDisabled : function(state)
6838     {
6839         if(this.disabled == state){
6840             return;
6841         }
6842         
6843         this.disabled = state;
6844         
6845         if (state) {
6846             this.el.addClass('disabled');
6847             return;
6848         }
6849         
6850         this.el.removeClass('disabled');
6851         
6852         return;
6853     },
6854     
6855     setActive : function(state)
6856     {
6857         if(this.active == state){
6858             return;
6859         }
6860         
6861         this.active = state;
6862         
6863         if (state) {
6864             this.el.addClass('active');
6865             return;
6866         }
6867         
6868         this.el.removeClass('active');
6869         
6870         return;
6871     },
6872     
6873     isActive: function () 
6874     {
6875         return this.active;
6876     },
6877     
6878     setBadge : function(str)
6879     {
6880         if(!this.badgeEl){
6881             return;
6882         }
6883         
6884         this.badgeEl.dom.innerHTML = str;
6885     }
6886     
6887    
6888      
6889  
6890 });
6891  
6892
6893  /*
6894  * - LGPL
6895  *
6896  *  Breadcrumb Nav
6897  * 
6898  */
6899 Roo.namespace('Roo.bootstrap.breadcrumb');
6900
6901
6902 /**
6903  * @class Roo.bootstrap.breadcrumb.Nav
6904  * @extends Roo.bootstrap.Component
6905  * Bootstrap Breadcrumb Nav Class
6906  *  
6907  * @children Roo.bootstrap.breadcrumb.Item
6908  * 
6909  * @constructor
6910  * Create a new breadcrumb.Nav
6911  * @param {Object} config The config object
6912  */
6913
6914
6915 Roo.bootstrap.breadcrumb.Nav = function(config){
6916     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6917     
6918     
6919 };
6920
6921 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6922     
6923     getAutoCreate : function()
6924     {
6925
6926         var cfg = {
6927             tag: 'nav',
6928             cn : [
6929                 {
6930                     tag : 'ol',
6931                     cls : 'breadcrumb'
6932                 }
6933             ]
6934             
6935         };
6936           
6937         return cfg;
6938     },
6939     
6940     initEvents: function()
6941     {
6942         this.olEl = this.el.select('ol',true).first();    
6943     },
6944     getChildContainer : function()
6945     {
6946         return this.olEl;  
6947     }
6948     
6949 });
6950
6951  /*
6952  * - LGPL
6953  *
6954  *  Breadcrumb Item
6955  * 
6956  */
6957
6958
6959 /**
6960  * @class Roo.bootstrap.breadcrumb.Nav
6961  * @extends Roo.bootstrap.Component
6962  * Bootstrap Breadcrumb Nav Class
6963  *  
6964  * @children Roo.bootstrap.breadcrumb.Component
6965  * @cfg {String} html the content of the link.
6966  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6967  * @cfg {Boolean} active is it active
6968
6969  * 
6970  * @constructor
6971  * Create a new breadcrumb.Nav
6972  * @param {Object} config The config object
6973  */
6974
6975 Roo.bootstrap.breadcrumb.Item = function(config){
6976     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6977     this.addEvents({
6978         // img events
6979         /**
6980          * @event click
6981          * The img click event for the img.
6982          * @param {Roo.EventObject} e
6983          */
6984         "click" : true
6985     });
6986     
6987 };
6988
6989 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6990     
6991     href: false,
6992     html : '',
6993     
6994     getAutoCreate : function()
6995     {
6996
6997         var cfg = {
6998             tag: 'li',
6999             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7000         };
7001         if (this.href !== false) {
7002             cfg.cn = [{
7003                 tag : 'a',
7004                 href : this.href,
7005                 html : this.html
7006             }];
7007         } else {
7008             cfg.html = this.html;
7009         }
7010         
7011         return cfg;
7012     },
7013     
7014     initEvents: function()
7015     {
7016         if (this.href) {
7017             this.el.select('a', true).first().on('click',this.onClick, this)
7018         }
7019         
7020     },
7021     onClick : function(e)
7022     {
7023         e.preventDefault();
7024         this.fireEvent('click',this,  e);
7025     }
7026     
7027 });
7028
7029  /*
7030  * - LGPL
7031  *
7032  * row
7033  * 
7034  */
7035
7036 /**
7037  * @class Roo.bootstrap.Row
7038  * @extends Roo.bootstrap.Component
7039  * Bootstrap Row class (contains columns...)
7040  * 
7041  * @constructor
7042  * Create a new Row
7043  * @param {Object} config The config object
7044  */
7045
7046 Roo.bootstrap.Row = function(config){
7047     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7048 };
7049
7050 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7051     
7052     getAutoCreate : function(){
7053        return {
7054             cls: 'row clearfix'
7055        };
7056     }
7057     
7058     
7059 });
7060
7061  
7062
7063  /*
7064  * - LGPL
7065  *
7066  * pagination
7067  * 
7068  */
7069
7070 /**
7071  * @class Roo.bootstrap.Pagination
7072  * @extends Roo.bootstrap.Component
7073  * Bootstrap Pagination class
7074  * @cfg {String} size xs | sm | md | lg
7075  * @cfg {Boolean} inverse false | true
7076  * 
7077  * @constructor
7078  * Create a new Pagination
7079  * @param {Object} config The config object
7080  */
7081
7082 Roo.bootstrap.Pagination = function(config){
7083     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7084 };
7085
7086 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7087     
7088     cls: false,
7089     size: false,
7090     inverse: false,
7091     
7092     getAutoCreate : function(){
7093         var cfg = {
7094             tag: 'ul',
7095                 cls: 'pagination'
7096         };
7097         if (this.inverse) {
7098             cfg.cls += ' inverse';
7099         }
7100         if (this.html) {
7101             cfg.html=this.html;
7102         }
7103         if (this.cls) {
7104             cfg.cls += " " + this.cls;
7105         }
7106         return cfg;
7107     }
7108    
7109 });
7110
7111  
7112
7113  /*
7114  * - LGPL
7115  *
7116  * Pagination item
7117  * 
7118  */
7119
7120
7121 /**
7122  * @class Roo.bootstrap.PaginationItem
7123  * @extends Roo.bootstrap.Component
7124  * Bootstrap PaginationItem class
7125  * @cfg {String} html text
7126  * @cfg {String} href the link
7127  * @cfg {Boolean} preventDefault (true | false) default true
7128  * @cfg {Boolean} active (true | false) default false
7129  * @cfg {Boolean} disabled default false
7130  * 
7131  * 
7132  * @constructor
7133  * Create a new PaginationItem
7134  * @param {Object} config The config object
7135  */
7136
7137
7138 Roo.bootstrap.PaginationItem = function(config){
7139     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7140     this.addEvents({
7141         // raw events
7142         /**
7143          * @event click
7144          * The raw click event for the entire grid.
7145          * @param {Roo.EventObject} e
7146          */
7147         "click" : true
7148     });
7149 };
7150
7151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7152     
7153     href : false,
7154     html : false,
7155     preventDefault: true,
7156     active : false,
7157     cls : false,
7158     disabled: false,
7159     
7160     getAutoCreate : function(){
7161         var cfg= {
7162             tag: 'li',
7163             cn: [
7164                 {
7165                     tag : 'a',
7166                     href : this.href ? this.href : '#',
7167                     html : this.html ? this.html : ''
7168                 }
7169             ]
7170         };
7171         
7172         if(this.cls){
7173             cfg.cls = this.cls;
7174         }
7175         
7176         if(this.disabled){
7177             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7178         }
7179         
7180         if(this.active){
7181             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7182         }
7183         
7184         return cfg;
7185     },
7186     
7187     initEvents: function() {
7188         
7189         this.el.on('click', this.onClick, this);
7190         
7191     },
7192     onClick : function(e)
7193     {
7194         Roo.log('PaginationItem on click ');
7195         if(this.preventDefault){
7196             e.preventDefault();
7197         }
7198         
7199         if(this.disabled){
7200             return;
7201         }
7202         
7203         this.fireEvent('click', this, e);
7204     }
7205    
7206 });
7207
7208  
7209
7210  /*
7211  * - LGPL
7212  *
7213  * slider
7214  * 
7215  */
7216
7217
7218 /**
7219  * @class Roo.bootstrap.Slider
7220  * @extends Roo.bootstrap.Component
7221  * Bootstrap Slider class
7222  *    
7223  * @constructor
7224  * Create a new Slider
7225  * @param {Object} config The config object
7226  */
7227
7228 Roo.bootstrap.Slider = function(config){
7229     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7230 };
7231
7232 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7233     
7234     getAutoCreate : function(){
7235         
7236         var cfg = {
7237             tag: 'div',
7238             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7239             cn: [
7240                 {
7241                     tag: 'a',
7242                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7243                 }
7244             ]
7245         };
7246         
7247         return cfg;
7248     }
7249    
7250 });
7251
7252  /*
7253  * Based on:
7254  * Ext JS Library 1.1.1
7255  * Copyright(c) 2006-2007, Ext JS, LLC.
7256  *
7257  * Originally Released Under LGPL - original licence link has changed is not relivant.
7258  *
7259  * Fork - LGPL
7260  * <script type="text/javascript">
7261  */
7262  
7263
7264 /**
7265  * @class Roo.grid.ColumnModel
7266  * @extends Roo.util.Observable
7267  * This is the default implementation of a ColumnModel used by the Grid. It defines
7268  * the columns in the grid.
7269  * <br>Usage:<br>
7270  <pre><code>
7271  var colModel = new Roo.grid.ColumnModel([
7272         {header: "Ticker", width: 60, sortable: true, locked: true},
7273         {header: "Company Name", width: 150, sortable: true},
7274         {header: "Market Cap.", width: 100, sortable: true},
7275         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7276         {header: "Employees", width: 100, sortable: true, resizable: false}
7277  ]);
7278  </code></pre>
7279  * <p>
7280  
7281  * The config options listed for this class are options which may appear in each
7282  * individual column definition.
7283  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7284  * @constructor
7285  * @param {Object} config An Array of column config objects. See this class's
7286  * config objects for details.
7287 */
7288 Roo.grid.ColumnModel = function(config){
7289         /**
7290      * The config passed into the constructor
7291      */
7292     this.config = config;
7293     this.lookup = {};
7294
7295     // if no id, create one
7296     // if the column does not have a dataIndex mapping,
7297     // map it to the order it is in the config
7298     for(var i = 0, len = config.length; i < len; i++){
7299         var c = config[i];
7300         if(typeof c.dataIndex == "undefined"){
7301             c.dataIndex = i;
7302         }
7303         if(typeof c.renderer == "string"){
7304             c.renderer = Roo.util.Format[c.renderer];
7305         }
7306         if(typeof c.id == "undefined"){
7307             c.id = Roo.id();
7308         }
7309         if(c.editor && c.editor.xtype){
7310             c.editor  = Roo.factory(c.editor, Roo.grid);
7311         }
7312         if(c.editor && c.editor.isFormField){
7313             c.editor = new Roo.grid.GridEditor(c.editor);
7314         }
7315         this.lookup[c.id] = c;
7316     }
7317
7318     /**
7319      * The width of columns which have no width specified (defaults to 100)
7320      * @type Number
7321      */
7322     this.defaultWidth = 100;
7323
7324     /**
7325      * Default sortable of columns which have no sortable specified (defaults to false)
7326      * @type Boolean
7327      */
7328     this.defaultSortable = false;
7329
7330     this.addEvents({
7331         /**
7332              * @event widthchange
7333              * Fires when the width of a column changes.
7334              * @param {ColumnModel} this
7335              * @param {Number} columnIndex The column index
7336              * @param {Number} newWidth The new width
7337              */
7338             "widthchange": true,
7339         /**
7340              * @event headerchange
7341              * Fires when the text of a header changes.
7342              * @param {ColumnModel} this
7343              * @param {Number} columnIndex The column index
7344              * @param {Number} newText The new header text
7345              */
7346             "headerchange": true,
7347         /**
7348              * @event hiddenchange
7349              * Fires when a column is hidden or "unhidden".
7350              * @param {ColumnModel} this
7351              * @param {Number} columnIndex The column index
7352              * @param {Boolean} hidden true if hidden, false otherwise
7353              */
7354             "hiddenchange": true,
7355             /**
7356          * @event columnmoved
7357          * Fires when a column is moved.
7358          * @param {ColumnModel} this
7359          * @param {Number} oldIndex
7360          * @param {Number} newIndex
7361          */
7362         "columnmoved" : true,
7363         /**
7364          * @event columlockchange
7365          * Fires when a column's locked state is changed
7366          * @param {ColumnModel} this
7367          * @param {Number} colIndex
7368          * @param {Boolean} locked true if locked
7369          */
7370         "columnlockchange" : true
7371     });
7372     Roo.grid.ColumnModel.superclass.constructor.call(this);
7373 };
7374 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7375     /**
7376      * @cfg {String} header The header text to display in the Grid view.
7377      */
7378     /**
7379      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7380      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7381      * specified, the column's index is used as an index into the Record's data Array.
7382      */
7383     /**
7384      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7385      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7386      */
7387     /**
7388      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7389      * Defaults to the value of the {@link #defaultSortable} property.
7390      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7391      */
7392     /**
7393      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7394      */
7395     /**
7396      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7397      */
7398     /**
7399      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7400      */
7401     /**
7402      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7403      */
7404     /**
7405      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7406      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7407      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7408      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7409      */
7410        /**
7411      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7412      */
7413     /**
7414      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7415      */
7416     /**
7417      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7418      */
7419     /**
7420      * @cfg {String} cursor (Optional)
7421      */
7422     /**
7423      * @cfg {String} tooltip (Optional)
7424      */
7425     /**
7426      * @cfg {Number} xs (Optional)
7427      */
7428     /**
7429      * @cfg {Number} sm (Optional)
7430      */
7431     /**
7432      * @cfg {Number} md (Optional)
7433      */
7434     /**
7435      * @cfg {Number} lg (Optional)
7436      */
7437     /**
7438      * Returns the id of the column at the specified index.
7439      * @param {Number} index The column index
7440      * @return {String} the id
7441      */
7442     getColumnId : function(index){
7443         return this.config[index].id;
7444     },
7445
7446     /**
7447      * Returns the column for a specified id.
7448      * @param {String} id The column id
7449      * @return {Object} the column
7450      */
7451     getColumnById : function(id){
7452         return this.lookup[id];
7453     },
7454
7455     
7456     /**
7457      * Returns the column for a specified dataIndex.
7458      * @param {String} dataIndex The column dataIndex
7459      * @return {Object|Boolean} the column or false if not found
7460      */
7461     getColumnByDataIndex: function(dataIndex){
7462         var index = this.findColumnIndex(dataIndex);
7463         return index > -1 ? this.config[index] : false;
7464     },
7465     
7466     /**
7467      * Returns the index for a specified column id.
7468      * @param {String} id The column id
7469      * @return {Number} the index, or -1 if not found
7470      */
7471     getIndexById : function(id){
7472         for(var i = 0, len = this.config.length; i < len; i++){
7473             if(this.config[i].id == id){
7474                 return i;
7475             }
7476         }
7477         return -1;
7478     },
7479     
7480     /**
7481      * Returns the index for a specified column dataIndex.
7482      * @param {String} dataIndex The column dataIndex
7483      * @return {Number} the index, or -1 if not found
7484      */
7485     
7486     findColumnIndex : function(dataIndex){
7487         for(var i = 0, len = this.config.length; i < len; i++){
7488             if(this.config[i].dataIndex == dataIndex){
7489                 return i;
7490             }
7491         }
7492         return -1;
7493     },
7494     
7495     
7496     moveColumn : function(oldIndex, newIndex){
7497         var c = this.config[oldIndex];
7498         this.config.splice(oldIndex, 1);
7499         this.config.splice(newIndex, 0, c);
7500         this.dataMap = null;
7501         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7502     },
7503
7504     isLocked : function(colIndex){
7505         return this.config[colIndex].locked === true;
7506     },
7507
7508     setLocked : function(colIndex, value, suppressEvent){
7509         if(this.isLocked(colIndex) == value){
7510             return;
7511         }
7512         this.config[colIndex].locked = value;
7513         if(!suppressEvent){
7514             this.fireEvent("columnlockchange", this, colIndex, value);
7515         }
7516     },
7517
7518     getTotalLockedWidth : function(){
7519         var totalWidth = 0;
7520         for(var i = 0; i < this.config.length; i++){
7521             if(this.isLocked(i) && !this.isHidden(i)){
7522                 this.totalWidth += this.getColumnWidth(i);
7523             }
7524         }
7525         return totalWidth;
7526     },
7527
7528     getLockedCount : function(){
7529         for(var i = 0, len = this.config.length; i < len; i++){
7530             if(!this.isLocked(i)){
7531                 return i;
7532             }
7533         }
7534         
7535         return this.config.length;
7536     },
7537
7538     /**
7539      * Returns the number of columns.
7540      * @return {Number}
7541      */
7542     getColumnCount : function(visibleOnly){
7543         if(visibleOnly === true){
7544             var c = 0;
7545             for(var i = 0, len = this.config.length; i < len; i++){
7546                 if(!this.isHidden(i)){
7547                     c++;
7548                 }
7549             }
7550             return c;
7551         }
7552         return this.config.length;
7553     },
7554
7555     /**
7556      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7557      * @param {Function} fn
7558      * @param {Object} scope (optional)
7559      * @return {Array} result
7560      */
7561     getColumnsBy : function(fn, scope){
7562         var r = [];
7563         for(var i = 0, len = this.config.length; i < len; i++){
7564             var c = this.config[i];
7565             if(fn.call(scope||this, c, i) === true){
7566                 r[r.length] = c;
7567             }
7568         }
7569         return r;
7570     },
7571
7572     /**
7573      * Returns true if the specified column is sortable.
7574      * @param {Number} col The column index
7575      * @return {Boolean}
7576      */
7577     isSortable : function(col){
7578         if(typeof this.config[col].sortable == "undefined"){
7579             return this.defaultSortable;
7580         }
7581         return this.config[col].sortable;
7582     },
7583
7584     /**
7585      * Returns the rendering (formatting) function defined for the column.
7586      * @param {Number} col The column index.
7587      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7588      */
7589     getRenderer : function(col){
7590         if(!this.config[col].renderer){
7591             return Roo.grid.ColumnModel.defaultRenderer;
7592         }
7593         return this.config[col].renderer;
7594     },
7595
7596     /**
7597      * Sets the rendering (formatting) function for a column.
7598      * @param {Number} col The column index
7599      * @param {Function} fn The function to use to process the cell's raw data
7600      * to return HTML markup for the grid view. The render function is called with
7601      * the following parameters:<ul>
7602      * <li>Data value.</li>
7603      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7604      * <li>css A CSS style string to apply to the table cell.</li>
7605      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7606      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7607      * <li>Row index</li>
7608      * <li>Column index</li>
7609      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7610      */
7611     setRenderer : function(col, fn){
7612         this.config[col].renderer = fn;
7613     },
7614
7615     /**
7616      * Returns the width for the specified column.
7617      * @param {Number} col The column index
7618      * @return {Number}
7619      */
7620     getColumnWidth : function(col){
7621         return this.config[col].width * 1 || this.defaultWidth;
7622     },
7623
7624     /**
7625      * Sets the width for a column.
7626      * @param {Number} col The column index
7627      * @param {Number} width The new width
7628      */
7629     setColumnWidth : function(col, width, suppressEvent){
7630         this.config[col].width = width;
7631         this.totalWidth = null;
7632         if(!suppressEvent){
7633              this.fireEvent("widthchange", this, col, width);
7634         }
7635     },
7636
7637     /**
7638      * Returns the total width of all columns.
7639      * @param {Boolean} includeHidden True to include hidden column widths
7640      * @return {Number}
7641      */
7642     getTotalWidth : function(includeHidden){
7643         if(!this.totalWidth){
7644             this.totalWidth = 0;
7645             for(var i = 0, len = this.config.length; i < len; i++){
7646                 if(includeHidden || !this.isHidden(i)){
7647                     this.totalWidth += this.getColumnWidth(i);
7648                 }
7649             }
7650         }
7651         return this.totalWidth;
7652     },
7653
7654     /**
7655      * Returns the header for the specified column.
7656      * @param {Number} col The column index
7657      * @return {String}
7658      */
7659     getColumnHeader : function(col){
7660         return this.config[col].header;
7661     },
7662
7663     /**
7664      * Sets the header for a column.
7665      * @param {Number} col The column index
7666      * @param {String} header The new header
7667      */
7668     setColumnHeader : function(col, header){
7669         this.config[col].header = header;
7670         this.fireEvent("headerchange", this, col, header);
7671     },
7672
7673     /**
7674      * Returns the tooltip for the specified column.
7675      * @param {Number} col The column index
7676      * @return {String}
7677      */
7678     getColumnTooltip : function(col){
7679             return this.config[col].tooltip;
7680     },
7681     /**
7682      * Sets the tooltip for a column.
7683      * @param {Number} col The column index
7684      * @param {String} tooltip The new tooltip
7685      */
7686     setColumnTooltip : function(col, tooltip){
7687             this.config[col].tooltip = tooltip;
7688     },
7689
7690     /**
7691      * Returns the dataIndex for the specified column.
7692      * @param {Number} col The column index
7693      * @return {Number}
7694      */
7695     getDataIndex : function(col){
7696         return this.config[col].dataIndex;
7697     },
7698
7699     /**
7700      * Sets the dataIndex for a column.
7701      * @param {Number} col The column index
7702      * @param {Number} dataIndex The new dataIndex
7703      */
7704     setDataIndex : function(col, dataIndex){
7705         this.config[col].dataIndex = dataIndex;
7706     },
7707
7708     
7709     
7710     /**
7711      * Returns true if the cell is editable.
7712      * @param {Number} colIndex The column index
7713      * @param {Number} rowIndex The row index - this is nto actually used..?
7714      * @return {Boolean}
7715      */
7716     isCellEditable : function(colIndex, rowIndex){
7717         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7718     },
7719
7720     /**
7721      * Returns the editor defined for the cell/column.
7722      * return false or null to disable editing.
7723      * @param {Number} colIndex The column index
7724      * @param {Number} rowIndex The row index
7725      * @return {Object}
7726      */
7727     getCellEditor : function(colIndex, rowIndex){
7728         return this.config[colIndex].editor;
7729     },
7730
7731     /**
7732      * Sets if a column is editable.
7733      * @param {Number} col The column index
7734      * @param {Boolean} editable True if the column is editable
7735      */
7736     setEditable : function(col, editable){
7737         this.config[col].editable = editable;
7738     },
7739
7740
7741     /**
7742      * Returns true if the column is hidden.
7743      * @param {Number} colIndex The column index
7744      * @return {Boolean}
7745      */
7746     isHidden : function(colIndex){
7747         return this.config[colIndex].hidden;
7748     },
7749
7750
7751     /**
7752      * Returns true if the column width cannot be changed
7753      */
7754     isFixed : function(colIndex){
7755         return this.config[colIndex].fixed;
7756     },
7757
7758     /**
7759      * Returns true if the column can be resized
7760      * @return {Boolean}
7761      */
7762     isResizable : function(colIndex){
7763         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7764     },
7765     /**
7766      * Sets if a column is hidden.
7767      * @param {Number} colIndex The column index
7768      * @param {Boolean} hidden True if the column is hidden
7769      */
7770     setHidden : function(colIndex, hidden){
7771         this.config[colIndex].hidden = hidden;
7772         this.totalWidth = null;
7773         this.fireEvent("hiddenchange", this, colIndex, hidden);
7774     },
7775
7776     /**
7777      * Sets the editor for a column.
7778      * @param {Number} col The column index
7779      * @param {Object} editor The editor object
7780      */
7781     setEditor : function(col, editor){
7782         this.config[col].editor = editor;
7783     }
7784 });
7785
7786 Roo.grid.ColumnModel.defaultRenderer = function(value)
7787 {
7788     if(typeof value == "object") {
7789         return value;
7790     }
7791         if(typeof value == "string" && value.length < 1){
7792             return "&#160;";
7793         }
7794     
7795         return String.format("{0}", value);
7796 };
7797
7798 // Alias for backwards compatibility
7799 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7800 /*
7801  * Based on:
7802  * Ext JS Library 1.1.1
7803  * Copyright(c) 2006-2007, Ext JS, LLC.
7804  *
7805  * Originally Released Under LGPL - original licence link has changed is not relivant.
7806  *
7807  * Fork - LGPL
7808  * <script type="text/javascript">
7809  */
7810  
7811 /**
7812  * @class Roo.LoadMask
7813  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7814  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7815  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7816  * element's UpdateManager load indicator and will be destroyed after the initial load.
7817  * @constructor
7818  * Create a new LoadMask
7819  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7820  * @param {Object} config The config object
7821  */
7822 Roo.LoadMask = function(el, config){
7823     this.el = Roo.get(el);
7824     Roo.apply(this, config);
7825     if(this.store){
7826         this.store.on('beforeload', this.onBeforeLoad, this);
7827         this.store.on('load', this.onLoad, this);
7828         this.store.on('loadexception', this.onLoadException, this);
7829         this.removeMask = false;
7830     }else{
7831         var um = this.el.getUpdateManager();
7832         um.showLoadIndicator = false; // disable the default indicator
7833         um.on('beforeupdate', this.onBeforeLoad, this);
7834         um.on('update', this.onLoad, this);
7835         um.on('failure', this.onLoad, this);
7836         this.removeMask = true;
7837     }
7838 };
7839
7840 Roo.LoadMask.prototype = {
7841     /**
7842      * @cfg {Boolean} removeMask
7843      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7844      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7845      */
7846     /**
7847      * @cfg {String} msg
7848      * The text to display in a centered loading message box (defaults to 'Loading...')
7849      */
7850     msg : 'Loading...',
7851     /**
7852      * @cfg {String} msgCls
7853      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7854      */
7855     msgCls : 'x-mask-loading',
7856
7857     /**
7858      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7859      * @type Boolean
7860      */
7861     disabled: false,
7862
7863     /**
7864      * Disables the mask to prevent it from being displayed
7865      */
7866     disable : function(){
7867        this.disabled = true;
7868     },
7869
7870     /**
7871      * Enables the mask so that it can be displayed
7872      */
7873     enable : function(){
7874         this.disabled = false;
7875     },
7876     
7877     onLoadException : function()
7878     {
7879         Roo.log(arguments);
7880         
7881         if (typeof(arguments[3]) != 'undefined') {
7882             Roo.MessageBox.alert("Error loading",arguments[3]);
7883         } 
7884         /*
7885         try {
7886             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7887                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7888             }   
7889         } catch(e) {
7890             
7891         }
7892         */
7893     
7894         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7895     },
7896     // private
7897     onLoad : function()
7898     {
7899         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7900     },
7901
7902     // private
7903     onBeforeLoad : function(){
7904         if(!this.disabled){
7905             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7906         }
7907     },
7908
7909     // private
7910     destroy : function(){
7911         if(this.store){
7912             this.store.un('beforeload', this.onBeforeLoad, this);
7913             this.store.un('load', this.onLoad, this);
7914             this.store.un('loadexception', this.onLoadException, this);
7915         }else{
7916             var um = this.el.getUpdateManager();
7917             um.un('beforeupdate', this.onBeforeLoad, this);
7918             um.un('update', this.onLoad, this);
7919             um.un('failure', this.onLoad, this);
7920         }
7921     }
7922 };/*
7923  * - LGPL
7924  *
7925  * table
7926  * 
7927  */
7928
7929 /**
7930  * @class Roo.bootstrap.Table
7931  * @extends Roo.bootstrap.Component
7932  * Bootstrap Table class
7933  * @cfg {String} cls table class
7934  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7935  * @cfg {String} bgcolor Specifies the background color for a table
7936  * @cfg {Number} border Specifies whether the table cells should have borders or not
7937  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7938  * @cfg {Number} cellspacing Specifies the space between cells
7939  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7940  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7941  * @cfg {String} sortable Specifies that the table should be sortable
7942  * @cfg {String} summary Specifies a summary of the content of a table
7943  * @cfg {Number} width Specifies the width of a table
7944  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7945  * 
7946  * @cfg {boolean} striped Should the rows be alternative striped
7947  * @cfg {boolean} bordered Add borders to the table
7948  * @cfg {boolean} hover Add hover highlighting
7949  * @cfg {boolean} condensed Format condensed
7950  * @cfg {boolean} responsive Format condensed
7951  * @cfg {Boolean} loadMask (true|false) default false
7952  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7953  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7954  * @cfg {Boolean} rowSelection (true|false) default false
7955  * @cfg {Boolean} cellSelection (true|false) default false
7956  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7957  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7958  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7959  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7960  
7961  * 
7962  * @constructor
7963  * Create a new Table
7964  * @param {Object} config The config object
7965  */
7966
7967 Roo.bootstrap.Table = function(config){
7968     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7969     
7970   
7971     
7972     // BC...
7973     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7974     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7975     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7976     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7977     
7978     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7979     if (this.sm) {
7980         this.sm.grid = this;
7981         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7982         this.sm = this.selModel;
7983         this.sm.xmodule = this.xmodule || false;
7984     }
7985     
7986     if (this.cm && typeof(this.cm.config) == 'undefined') {
7987         this.colModel = new Roo.grid.ColumnModel(this.cm);
7988         this.cm = this.colModel;
7989         this.cm.xmodule = this.xmodule || false;
7990     }
7991     if (this.store) {
7992         this.store= Roo.factory(this.store, Roo.data);
7993         this.ds = this.store;
7994         this.ds.xmodule = this.xmodule || false;
7995          
7996     }
7997     if (this.footer && this.store) {
7998         this.footer.dataSource = this.ds;
7999         this.footer = Roo.factory(this.footer);
8000     }
8001     
8002     /** @private */
8003     this.addEvents({
8004         /**
8005          * @event cellclick
8006          * Fires when a cell is clicked
8007          * @param {Roo.bootstrap.Table} this
8008          * @param {Roo.Element} el
8009          * @param {Number} rowIndex
8010          * @param {Number} columnIndex
8011          * @param {Roo.EventObject} e
8012          */
8013         "cellclick" : true,
8014         /**
8015          * @event celldblclick
8016          * Fires when a cell is double clicked
8017          * @param {Roo.bootstrap.Table} this
8018          * @param {Roo.Element} el
8019          * @param {Number} rowIndex
8020          * @param {Number} columnIndex
8021          * @param {Roo.EventObject} e
8022          */
8023         "celldblclick" : true,
8024         /**
8025          * @event rowclick
8026          * Fires when a row is clicked
8027          * @param {Roo.bootstrap.Table} this
8028          * @param {Roo.Element} el
8029          * @param {Number} rowIndex
8030          * @param {Roo.EventObject} e
8031          */
8032         "rowclick" : true,
8033         /**
8034          * @event rowdblclick
8035          * Fires when a row is double clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Roo.EventObject} e
8040          */
8041         "rowdblclick" : true,
8042         /**
8043          * @event mouseover
8044          * Fires when a mouseover occur
8045          * @param {Roo.bootstrap.Table} this
8046          * @param {Roo.Element} el
8047          * @param {Number} rowIndex
8048          * @param {Number} columnIndex
8049          * @param {Roo.EventObject} e
8050          */
8051         "mouseover" : true,
8052         /**
8053          * @event mouseout
8054          * Fires when a mouseout occur
8055          * @param {Roo.bootstrap.Table} this
8056          * @param {Roo.Element} el
8057          * @param {Number} rowIndex
8058          * @param {Number} columnIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "mouseout" : true,
8062         /**
8063          * @event rowclass
8064          * Fires when a row is rendered, so you can change add a style to it.
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8067          */
8068         'rowclass' : true,
8069           /**
8070          * @event rowsrendered
8071          * Fires when all the  rows have been rendered
8072          * @param {Roo.bootstrap.Table} this
8073          */
8074         'rowsrendered' : true,
8075         /**
8076          * @event contextmenu
8077          * The raw contextmenu event for the entire grid.
8078          * @param {Roo.EventObject} e
8079          */
8080         "contextmenu" : true,
8081         /**
8082          * @event rowcontextmenu
8083          * Fires when a row is right clicked
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Number} rowIndex
8086          * @param {Roo.EventObject} e
8087          */
8088         "rowcontextmenu" : true,
8089         /**
8090          * @event cellcontextmenu
8091          * Fires when a cell is right clicked
8092          * @param {Roo.bootstrap.Table} this
8093          * @param {Number} rowIndex
8094          * @param {Number} cellIndex
8095          * @param {Roo.EventObject} e
8096          */
8097          "cellcontextmenu" : true,
8098          /**
8099          * @event headercontextmenu
8100          * Fires when a header is right clicked
8101          * @param {Roo.bootstrap.Table} this
8102          * @param {Number} columnIndex
8103          * @param {Roo.EventObject} e
8104          */
8105         "headercontextmenu" : true
8106     });
8107 };
8108
8109 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8110     
8111     cls: false,
8112     align: false,
8113     bgcolor: false,
8114     border: false,
8115     cellpadding: false,
8116     cellspacing: false,
8117     frame: false,
8118     rules: false,
8119     sortable: false,
8120     summary: false,
8121     width: false,
8122     striped : false,
8123     scrollBody : false,
8124     bordered: false,
8125     hover:  false,
8126     condensed : false,
8127     responsive : false,
8128     sm : false,
8129     cm : false,
8130     store : false,
8131     loadMask : false,
8132     footerShow : true,
8133     headerShow : true,
8134   
8135     rowSelection : false,
8136     cellSelection : false,
8137     layout : false,
8138     
8139     // Roo.Element - the tbody
8140     mainBody: false,
8141     // Roo.Element - thead element
8142     mainHead: false,
8143     
8144     container: false, // used by gridpanel...
8145     
8146     lazyLoad : false,
8147     
8148     CSS : Roo.util.CSS,
8149     
8150     auto_hide_footer : false,
8151     
8152     getAutoCreate : function()
8153     {
8154         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8155         
8156         cfg = {
8157             tag: 'table',
8158             cls : 'table',
8159             cn : []
8160         };
8161         if (this.scrollBody) {
8162             cfg.cls += ' table-body-fixed';
8163         }    
8164         if (this.striped) {
8165             cfg.cls += ' table-striped';
8166         }
8167         
8168         if (this.hover) {
8169             cfg.cls += ' table-hover';
8170         }
8171         if (this.bordered) {
8172             cfg.cls += ' table-bordered';
8173         }
8174         if (this.condensed) {
8175             cfg.cls += ' table-condensed';
8176         }
8177         if (this.responsive) {
8178             cfg.cls += ' table-responsive';
8179         }
8180         
8181         if (this.cls) {
8182             cfg.cls+=  ' ' +this.cls;
8183         }
8184         
8185         // this lot should be simplifed...
8186         var _t = this;
8187         var cp = [
8188             'align',
8189             'bgcolor',
8190             'border',
8191             'cellpadding',
8192             'cellspacing',
8193             'frame',
8194             'rules',
8195             'sortable',
8196             'summary',
8197             'width'
8198         ].forEach(function(k) {
8199             if (_t[k]) {
8200                 cfg[k] = _t[k];
8201             }
8202         });
8203         
8204         
8205         if (this.layout) {
8206             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8207         }
8208         
8209         if(this.store || this.cm){
8210             if(this.headerShow){
8211                 cfg.cn.push(this.renderHeader());
8212             }
8213             
8214             cfg.cn.push(this.renderBody());
8215             
8216             if(this.footerShow){
8217                 cfg.cn.push(this.renderFooter());
8218             }
8219             // where does this come from?
8220             //cfg.cls+=  ' TableGrid';
8221         }
8222         
8223         return { cn : [ cfg ] };
8224     },
8225     
8226     initEvents : function()
8227     {   
8228         if(!this.store || !this.cm){
8229             return;
8230         }
8231         if (this.selModel) {
8232             this.selModel.initEvents();
8233         }
8234         
8235         
8236         //Roo.log('initEvents with ds!!!!');
8237         
8238         this.mainBody = this.el.select('tbody', true).first();
8239         this.mainHead = this.el.select('thead', true).first();
8240         this.mainFoot = this.el.select('tfoot', true).first();
8241         
8242         
8243         
8244         var _this = this;
8245         
8246         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8247             e.on('click', _this.sort, _this);
8248         });
8249         
8250         this.mainBody.on("click", this.onClick, this);
8251         this.mainBody.on("dblclick", this.onDblClick, this);
8252         
8253         // why is this done????? = it breaks dialogs??
8254         //this.parent().el.setStyle('position', 'relative');
8255         
8256         
8257         if (this.footer) {
8258             this.footer.parentId = this.id;
8259             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8260             
8261             if(this.lazyLoad){
8262                 this.el.select('tfoot tr td').first().addClass('hide');
8263             }
8264         } 
8265         
8266         if(this.loadMask) {
8267             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8268         }
8269         
8270         this.store.on('load', this.onLoad, this);
8271         this.store.on('beforeload', this.onBeforeLoad, this);
8272         this.store.on('update', this.onUpdate, this);
8273         this.store.on('add', this.onAdd, this);
8274         this.store.on("clear", this.clear, this);
8275         
8276         this.el.on("contextmenu", this.onContextMenu, this);
8277         
8278         this.mainBody.on('scroll', this.onBodyScroll, this);
8279         
8280         this.cm.on("headerchange", this.onHeaderChange, this);
8281         
8282         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8283         
8284     },
8285     
8286     onContextMenu : function(e, t)
8287     {
8288         this.processEvent("contextmenu", e);
8289     },
8290     
8291     processEvent : function(name, e)
8292     {
8293         if (name != 'touchstart' ) {
8294             this.fireEvent(name, e);    
8295         }
8296         
8297         var t = e.getTarget();
8298         
8299         var cell = Roo.get(t);
8300         
8301         if(!cell){
8302             return;
8303         }
8304         
8305         if(cell.findParent('tfoot', false, true)){
8306             return;
8307         }
8308         
8309         if(cell.findParent('thead', false, true)){
8310             
8311             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8312                 cell = Roo.get(t).findParent('th', false, true);
8313                 if (!cell) {
8314                     Roo.log("failed to find th in thead?");
8315                     Roo.log(e.getTarget());
8316                     return;
8317                 }
8318             }
8319             
8320             var cellIndex = cell.dom.cellIndex;
8321             
8322             var ename = name == 'touchstart' ? 'click' : name;
8323             this.fireEvent("header" + ename, this, cellIndex, e);
8324             
8325             return;
8326         }
8327         
8328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8329             cell = Roo.get(t).findParent('td', false, true);
8330             if (!cell) {
8331                 Roo.log("failed to find th in tbody?");
8332                 Roo.log(e.getTarget());
8333                 return;
8334             }
8335         }
8336         
8337         var row = cell.findParent('tr', false, true);
8338         var cellIndex = cell.dom.cellIndex;
8339         var rowIndex = row.dom.rowIndex - 1;
8340         
8341         if(row !== false){
8342             
8343             this.fireEvent("row" + name, this, rowIndex, e);
8344             
8345             if(cell !== false){
8346             
8347                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8348             }
8349         }
8350         
8351     },
8352     
8353     onMouseover : function(e, el)
8354     {
8355         var cell = Roo.get(el);
8356         
8357         if(!cell){
8358             return;
8359         }
8360         
8361         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362             cell = cell.findParent('td', false, true);
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1; // start from 0
8368         
8369         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8370         
8371     },
8372     
8373     onMouseout : function(e, el)
8374     {
8375         var cell = Roo.get(el);
8376         
8377         if(!cell){
8378             return;
8379         }
8380         
8381         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8382             cell = cell.findParent('td', false, true);
8383         }
8384         
8385         var row = cell.findParent('tr', false, true);
8386         var cellIndex = cell.dom.cellIndex;
8387         var rowIndex = row.dom.rowIndex - 1; // start from 0
8388         
8389         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8390         
8391     },
8392     
8393     onClick : function(e, el)
8394     {
8395         var cell = Roo.get(el);
8396         
8397         if(!cell || (!this.cellSelection && !this.rowSelection)){
8398             return;
8399         }
8400         
8401         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402             cell = cell.findParent('td', false, true);
8403         }
8404         
8405         if(!cell || typeof(cell) == 'undefined'){
8406             return;
8407         }
8408         
8409         var row = cell.findParent('tr', false, true);
8410         
8411         if(!row || typeof(row) == 'undefined'){
8412             return;
8413         }
8414         
8415         var cellIndex = cell.dom.cellIndex;
8416         var rowIndex = this.getRowIndex(row);
8417         
8418         // why??? - should these not be based on SelectionModel?
8419         if(this.cellSelection){
8420             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8421         }
8422         
8423         if(this.rowSelection){
8424             this.fireEvent('rowclick', this, row, rowIndex, e);
8425         }
8426         
8427         
8428     },
8429         
8430     onDblClick : function(e,el)
8431     {
8432         var cell = Roo.get(el);
8433         
8434         if(!cell || (!this.cellSelection && !this.rowSelection)){
8435             return;
8436         }
8437         
8438         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8439             cell = cell.findParent('td', false, true);
8440         }
8441         
8442         if(!cell || typeof(cell) == 'undefined'){
8443             return;
8444         }
8445         
8446         var row = cell.findParent('tr', false, true);
8447         
8448         if(!row || typeof(row) == 'undefined'){
8449             return;
8450         }
8451         
8452         var cellIndex = cell.dom.cellIndex;
8453         var rowIndex = this.getRowIndex(row);
8454         
8455         if(this.cellSelection){
8456             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8457         }
8458         
8459         if(this.rowSelection){
8460             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8461         }
8462     },
8463     
8464     sort : function(e,el)
8465     {
8466         var col = Roo.get(el);
8467         
8468         if(!col.hasClass('sortable')){
8469             return;
8470         }
8471         
8472         var sort = col.attr('sort');
8473         var dir = 'ASC';
8474         
8475         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8476             dir = 'DESC';
8477         }
8478         
8479         this.store.sortInfo = {field : sort, direction : dir};
8480         
8481         if (this.footer) {
8482             Roo.log("calling footer first");
8483             this.footer.onClick('first');
8484         } else {
8485         
8486             this.store.load({ params : { start : 0 } });
8487         }
8488     },
8489     
8490     renderHeader : function()
8491     {
8492         var header = {
8493             tag: 'thead',
8494             cn : []
8495         };
8496         
8497         var cm = this.cm;
8498         this.totalWidth = 0;
8499         
8500         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8501             
8502             var config = cm.config[i];
8503             
8504             var c = {
8505                 tag: 'th',
8506                 cls : 'x-hcol-' + i,
8507                 style : '',
8508                 html: cm.getColumnHeader(i)
8509             };
8510             
8511             var hh = '';
8512             
8513             if(typeof(config.sortable) != 'undefined' && config.sortable){
8514                 c.cls = 'sortable';
8515                 c.html = '<i class="glyphicon"></i>' + c.html;
8516             }
8517             
8518             // could use BS4 hidden-..-down 
8519             
8520             if(typeof(config.lgHeader) != 'undefined'){
8521                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8522             }
8523             
8524             if(typeof(config.mdHeader) != 'undefined'){
8525                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8526             }
8527             
8528             if(typeof(config.smHeader) != 'undefined'){
8529                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8530             }
8531             
8532             if(typeof(config.xsHeader) != 'undefined'){
8533                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8534             }
8535             
8536             if(hh.length){
8537                 c.html = hh;
8538             }
8539             
8540             if(typeof(config.tooltip) != 'undefined'){
8541                 c.tooltip = config.tooltip;
8542             }
8543             
8544             if(typeof(config.colspan) != 'undefined'){
8545                 c.colspan = config.colspan;
8546             }
8547             
8548             if(typeof(config.hidden) != 'undefined' && config.hidden){
8549                 c.style += ' display:none;';
8550             }
8551             
8552             if(typeof(config.dataIndex) != 'undefined'){
8553                 c.sort = config.dataIndex;
8554             }
8555             
8556            
8557             
8558             if(typeof(config.align) != 'undefined' && config.align.length){
8559                 c.style += ' text-align:' + config.align + ';';
8560             }
8561             
8562             if(typeof(config.width) != 'undefined'){
8563                 c.style += ' width:' + config.width + 'px;';
8564                 this.totalWidth += config.width;
8565             } else {
8566                 this.totalWidth += 100; // assume minimum of 100 per column?
8567             }
8568             
8569             if(typeof(config.cls) != 'undefined'){
8570                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8571             }
8572             
8573             ['xs','sm','md','lg'].map(function(size){
8574                 
8575                 if(typeof(config[size]) == 'undefined'){
8576                     return;
8577                 }
8578                  
8579                 if (!config[size]) { // 0 = hidden
8580                     // BS 4 '0' is treated as hide that column and below.
8581                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8582                     return;
8583                 }
8584                 
8585                 c.cls += ' col-' + size + '-' + config[size] + (
8586                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8587                 );
8588                 
8589                 
8590             });
8591             
8592             header.cn.push(c)
8593         }
8594         
8595         return header;
8596     },
8597     
8598     renderBody : function()
8599     {
8600         var body = {
8601             tag: 'tbody',
8602             cn : [
8603                 {
8604                     tag: 'tr',
8605                     cn : [
8606                         {
8607                             tag : 'td',
8608                             colspan :  this.cm.getColumnCount()
8609                         }
8610                     ]
8611                 }
8612             ]
8613         };
8614         
8615         return body;
8616     },
8617     
8618     renderFooter : function()
8619     {
8620         var footer = {
8621             tag: 'tfoot',
8622             cn : [
8623                 {
8624                     tag: 'tr',
8625                     cn : [
8626                         {
8627                             tag : 'td',
8628                             colspan :  this.cm.getColumnCount()
8629                         }
8630                     ]
8631                 }
8632             ]
8633         };
8634         
8635         return footer;
8636     },
8637     
8638     
8639     
8640     onLoad : function()
8641     {
8642 //        Roo.log('ds onload');
8643         this.clear();
8644         
8645         var _this = this;
8646         var cm = this.cm;
8647         var ds = this.store;
8648         
8649         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8650             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8651             if (_this.store.sortInfo) {
8652                     
8653                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8654                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8655                 }
8656                 
8657                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8658                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8659                 }
8660             }
8661         });
8662         
8663         var tbody =  this.mainBody;
8664               
8665         if(ds.getCount() > 0){
8666             ds.data.each(function(d,rowIndex){
8667                 var row =  this.renderRow(cm, ds, rowIndex);
8668                 
8669                 tbody.createChild(row);
8670                 
8671                 var _this = this;
8672                 
8673                 if(row.cellObjects.length){
8674                     Roo.each(row.cellObjects, function(r){
8675                         _this.renderCellObject(r);
8676                     })
8677                 }
8678                 
8679             }, this);
8680         }
8681         
8682         var tfoot = this.el.select('tfoot', true).first();
8683         
8684         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8685             
8686             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8687             
8688             var total = this.ds.getTotalCount();
8689             
8690             if(this.footer.pageSize < total){
8691                 this.mainFoot.show();
8692             }
8693         }
8694         
8695         Roo.each(this.el.select('tbody td', true).elements, function(e){
8696             e.on('mouseover', _this.onMouseover, _this);
8697         });
8698         
8699         Roo.each(this.el.select('tbody td', true).elements, function(e){
8700             e.on('mouseout', _this.onMouseout, _this);
8701         });
8702         this.fireEvent('rowsrendered', this);
8703         
8704         this.autoSize();
8705     },
8706     
8707     
8708     onUpdate : function(ds,record)
8709     {
8710         this.refreshRow(record);
8711         this.autoSize();
8712     },
8713     
8714     onRemove : function(ds, record, index, isUpdate){
8715         if(isUpdate !== true){
8716             this.fireEvent("beforerowremoved", this, index, record);
8717         }
8718         var bt = this.mainBody.dom;
8719         
8720         var rows = this.el.select('tbody > tr', true).elements;
8721         
8722         if(typeof(rows[index]) != 'undefined'){
8723             bt.removeChild(rows[index].dom);
8724         }
8725         
8726 //        if(bt.rows[index]){
8727 //            bt.removeChild(bt.rows[index]);
8728 //        }
8729         
8730         if(isUpdate !== true){
8731             //this.stripeRows(index);
8732             //this.syncRowHeights(index, index);
8733             //this.layout();
8734             this.fireEvent("rowremoved", this, index, record);
8735         }
8736     },
8737     
8738     onAdd : function(ds, records, rowIndex)
8739     {
8740         //Roo.log('on Add called');
8741         // - note this does not handle multiple adding very well..
8742         var bt = this.mainBody.dom;
8743         for (var i =0 ; i < records.length;i++) {
8744             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8745             //Roo.log(records[i]);
8746             //Roo.log(this.store.getAt(rowIndex+i));
8747             this.insertRow(this.store, rowIndex + i, false);
8748             return;
8749         }
8750         
8751     },
8752     
8753     
8754     refreshRow : function(record){
8755         var ds = this.store, index;
8756         if(typeof record == 'number'){
8757             index = record;
8758             record = ds.getAt(index);
8759         }else{
8760             index = ds.indexOf(record);
8761             if (index < 0) {
8762                 return; // should not happen - but seems to 
8763             }
8764         }
8765         this.insertRow(ds, index, true);
8766         this.autoSize();
8767         this.onRemove(ds, record, index+1, true);
8768         this.autoSize();
8769         //this.syncRowHeights(index, index);
8770         //this.layout();
8771         this.fireEvent("rowupdated", this, index, record);
8772     },
8773     
8774     insertRow : function(dm, rowIndex, isUpdate){
8775         
8776         if(!isUpdate){
8777             this.fireEvent("beforerowsinserted", this, rowIndex);
8778         }
8779             //var s = this.getScrollState();
8780         var row = this.renderRow(this.cm, this.store, rowIndex);
8781         // insert before rowIndex..
8782         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8783         
8784         var _this = this;
8785                 
8786         if(row.cellObjects.length){
8787             Roo.each(row.cellObjects, function(r){
8788                 _this.renderCellObject(r);
8789             })
8790         }
8791             
8792         if(!isUpdate){
8793             this.fireEvent("rowsinserted", this, rowIndex);
8794             //this.syncRowHeights(firstRow, lastRow);
8795             //this.stripeRows(firstRow);
8796             //this.layout();
8797         }
8798         
8799     },
8800     
8801     
8802     getRowDom : function(rowIndex)
8803     {
8804         var rows = this.el.select('tbody > tr', true).elements;
8805         
8806         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8807         
8808     },
8809     // returns the object tree for a tr..
8810   
8811     
8812     renderRow : function(cm, ds, rowIndex) 
8813     {
8814         var d = ds.getAt(rowIndex);
8815         
8816         var row = {
8817             tag : 'tr',
8818             cls : 'x-row-' + rowIndex,
8819             cn : []
8820         };
8821             
8822         var cellObjects = [];
8823         
8824         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8825             var config = cm.config[i];
8826             
8827             var renderer = cm.getRenderer(i);
8828             var value = '';
8829             var id = false;
8830             
8831             if(typeof(renderer) !== 'undefined'){
8832                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8833             }
8834             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8835             // and are rendered into the cells after the row is rendered - using the id for the element.
8836             
8837             if(typeof(value) === 'object'){
8838                 id = Roo.id();
8839                 cellObjects.push({
8840                     container : id,
8841                     cfg : value 
8842                 })
8843             }
8844             
8845             var rowcfg = {
8846                 record: d,
8847                 rowIndex : rowIndex,
8848                 colIndex : i,
8849                 rowClass : ''
8850             };
8851
8852             this.fireEvent('rowclass', this, rowcfg);
8853             
8854             var td = {
8855                 tag: 'td',
8856                 cls : rowcfg.rowClass + ' x-col-' + i,
8857                 style: '',
8858                 html: (typeof(value) === 'object') ? '' : value
8859             };
8860             
8861             if (id) {
8862                 td.id = id;
8863             }
8864             
8865             if(typeof(config.colspan) != 'undefined'){
8866                 td.colspan = config.colspan;
8867             }
8868             
8869             if(typeof(config.hidden) != 'undefined' && config.hidden){
8870                 td.style += ' display:none;';
8871             }
8872             
8873             if(typeof(config.align) != 'undefined' && config.align.length){
8874                 td.style += ' text-align:' + config.align + ';';
8875             }
8876             if(typeof(config.valign) != 'undefined' && config.valign.length){
8877                 td.style += ' vertical-align:' + config.valign + ';';
8878             }
8879             
8880             if(typeof(config.width) != 'undefined'){
8881                 td.style += ' width:' +  config.width + 'px;';
8882             }
8883             
8884             if(typeof(config.cursor) != 'undefined'){
8885                 td.style += ' cursor:' +  config.cursor + ';';
8886             }
8887             
8888             if(typeof(config.cls) != 'undefined'){
8889                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8890             }
8891             
8892             ['xs','sm','md','lg'].map(function(size){
8893                 
8894                 if(typeof(config[size]) == 'undefined'){
8895                     return;
8896                 }
8897                 
8898                 
8899                   
8900                 if (!config[size]) { // 0 = hidden
8901                     // BS 4 '0' is treated as hide that column and below.
8902                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8903                     return;
8904                 }
8905                 
8906                 td.cls += ' col-' + size + '-' + config[size] + (
8907                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8908                 );
8909                  
8910
8911             });
8912             
8913             row.cn.push(td);
8914            
8915         }
8916         
8917         row.cellObjects = cellObjects;
8918         
8919         return row;
8920           
8921     },
8922     
8923     
8924     
8925     onBeforeLoad : function()
8926     {
8927         
8928     },
8929      /**
8930      * Remove all rows
8931      */
8932     clear : function()
8933     {
8934         this.el.select('tbody', true).first().dom.innerHTML = '';
8935     },
8936     /**
8937      * Show or hide a row.
8938      * @param {Number} rowIndex to show or hide
8939      * @param {Boolean} state hide
8940      */
8941     setRowVisibility : function(rowIndex, state)
8942     {
8943         var bt = this.mainBody.dom;
8944         
8945         var rows = this.el.select('tbody > tr', true).elements;
8946         
8947         if(typeof(rows[rowIndex]) == 'undefined'){
8948             return;
8949         }
8950         rows[rowIndex].dom.style.display = state ? '' : 'none';
8951     },
8952     
8953     
8954     getSelectionModel : function(){
8955         if(!this.selModel){
8956             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8957         }
8958         return this.selModel;
8959     },
8960     /*
8961      * Render the Roo.bootstrap object from renderder
8962      */
8963     renderCellObject : function(r)
8964     {
8965         var _this = this;
8966         
8967         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8968         
8969         var t = r.cfg.render(r.container);
8970         
8971         if(r.cfg.cn){
8972             Roo.each(r.cfg.cn, function(c){
8973                 var child = {
8974                     container: t.getChildContainer(),
8975                     cfg: c
8976                 };
8977                 _this.renderCellObject(child);
8978             })
8979         }
8980     },
8981     
8982     getRowIndex : function(row)
8983     {
8984         var rowIndex = -1;
8985         
8986         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8987             if(el != row){
8988                 return;
8989             }
8990             
8991             rowIndex = index;
8992         });
8993         
8994         return rowIndex;
8995     },
8996      /**
8997      * Returns the grid's underlying element = used by panel.Grid
8998      * @return {Element} The element
8999      */
9000     getGridEl : function(){
9001         return this.el;
9002     },
9003      /**
9004      * Forces a resize - used by panel.Grid
9005      * @return {Element} The element
9006      */
9007     autoSize : function()
9008     {
9009         //var ctr = Roo.get(this.container.dom.parentElement);
9010         var ctr = Roo.get(this.el.dom);
9011         
9012         var thd = this.getGridEl().select('thead',true).first();
9013         var tbd = this.getGridEl().select('tbody', true).first();
9014         var tfd = this.getGridEl().select('tfoot', true).first();
9015         
9016         var cw = ctr.getWidth();
9017         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9018         
9019         if (tbd) {
9020             
9021             tbd.setWidth(ctr.getWidth());
9022             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9023             // this needs fixing for various usage - currently only hydra job advers I think..
9024             //tdb.setHeight(
9025             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9026             //); 
9027             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9028             cw -= barsize;
9029         }
9030         cw = Math.max(cw, this.totalWidth);
9031         this.getGridEl().select('tbody tr',true).setWidth(cw);
9032         
9033         // resize 'expandable coloumn?
9034         
9035         return; // we doe not have a view in this design..
9036         
9037     },
9038     onBodyScroll: function()
9039     {
9040         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9041         if(this.mainHead){
9042             this.mainHead.setStyle({
9043                 'position' : 'relative',
9044                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9045             });
9046         }
9047         
9048         if(this.lazyLoad){
9049             
9050             var scrollHeight = this.mainBody.dom.scrollHeight;
9051             
9052             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9053             
9054             var height = this.mainBody.getHeight();
9055             
9056             if(scrollHeight - height == scrollTop) {
9057                 
9058                 var total = this.ds.getTotalCount();
9059                 
9060                 if(this.footer.cursor + this.footer.pageSize < total){
9061                     
9062                     this.footer.ds.load({
9063                         params : {
9064                             start : this.footer.cursor + this.footer.pageSize,
9065                             limit : this.footer.pageSize
9066                         },
9067                         add : true
9068                     });
9069                 }
9070             }
9071             
9072         }
9073     },
9074     
9075     onHeaderChange : function()
9076     {
9077         var header = this.renderHeader();
9078         var table = this.el.select('table', true).first();
9079         
9080         this.mainHead.remove();
9081         this.mainHead = table.createChild(header, this.mainBody, false);
9082     },
9083     
9084     onHiddenChange : function(colModel, colIndex, hidden)
9085     {
9086         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9087         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9088         
9089         this.CSS.updateRule(thSelector, "display", "");
9090         this.CSS.updateRule(tdSelector, "display", "");
9091         
9092         if(hidden){
9093             this.CSS.updateRule(thSelector, "display", "none");
9094             this.CSS.updateRule(tdSelector, "display", "none");
9095         }
9096         
9097         this.onHeaderChange();
9098         this.onLoad();
9099     },
9100     
9101     setColumnWidth: function(col_index, width)
9102     {
9103         // width = "md-2 xs-2..."
9104         if(!this.colModel.config[col_index]) {
9105             return;
9106         }
9107         
9108         var w = width.split(" ");
9109         
9110         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9111         
9112         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9113         
9114         
9115         for(var j = 0; j < w.length; j++) {
9116             
9117             if(!w[j]) {
9118                 continue;
9119             }
9120             
9121             var size_cls = w[j].split("-");
9122             
9123             if(!Number.isInteger(size_cls[1] * 1)) {
9124                 continue;
9125             }
9126             
9127             if(!this.colModel.config[col_index][size_cls[0]]) {
9128                 continue;
9129             }
9130             
9131             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9132                 continue;
9133             }
9134             
9135             h_row[0].classList.replace(
9136                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9137                 "col-"+size_cls[0]+"-"+size_cls[1]
9138             );
9139             
9140             for(var i = 0; i < rows.length; i++) {
9141                 
9142                 var size_cls = w[j].split("-");
9143                 
9144                 if(!Number.isInteger(size_cls[1] * 1)) {
9145                     continue;
9146                 }
9147                 
9148                 if(!this.colModel.config[col_index][size_cls[0]]) {
9149                     continue;
9150                 }
9151                 
9152                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9153                     continue;
9154                 }
9155                 
9156                 rows[i].classList.replace(
9157                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9158                     "col-"+size_cls[0]+"-"+size_cls[1]
9159                 );
9160             }
9161             
9162             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9163         }
9164     }
9165 });
9166
9167  
9168
9169  /*
9170  * - LGPL
9171  *
9172  * table cell
9173  * 
9174  */
9175
9176 /**
9177  * @class Roo.bootstrap.TableCell
9178  * @extends Roo.bootstrap.Component
9179  * Bootstrap TableCell class
9180  * @cfg {String} html cell contain text
9181  * @cfg {String} cls cell class
9182  * @cfg {String} tag cell tag (td|th) default td
9183  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9184  * @cfg {String} align Aligns the content in a cell
9185  * @cfg {String} axis Categorizes cells
9186  * @cfg {String} bgcolor Specifies the background color of a cell
9187  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9188  * @cfg {Number} colspan Specifies the number of columns a cell should span
9189  * @cfg {String} headers Specifies one or more header cells a cell is related to
9190  * @cfg {Number} height Sets the height of a cell
9191  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9192  * @cfg {Number} rowspan Sets the number of rows a cell should span
9193  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9194  * @cfg {String} valign Vertical aligns the content in a cell
9195  * @cfg {Number} width Specifies the width of a cell
9196  * 
9197  * @constructor
9198  * Create a new TableCell
9199  * @param {Object} config The config object
9200  */
9201
9202 Roo.bootstrap.TableCell = function(config){
9203     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9204 };
9205
9206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9207     
9208     html: false,
9209     cls: false,
9210     tag: false,
9211     abbr: false,
9212     align: false,
9213     axis: false,
9214     bgcolor: false,
9215     charoff: false,
9216     colspan: false,
9217     headers: false,
9218     height: false,
9219     nowrap: false,
9220     rowspan: false,
9221     scope: false,
9222     valign: false,
9223     width: false,
9224     
9225     
9226     getAutoCreate : function(){
9227         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9228         
9229         cfg = {
9230             tag: 'td'
9231         };
9232         
9233         if(this.tag){
9234             cfg.tag = this.tag;
9235         }
9236         
9237         if (this.html) {
9238             cfg.html=this.html
9239         }
9240         if (this.cls) {
9241             cfg.cls=this.cls
9242         }
9243         if (this.abbr) {
9244             cfg.abbr=this.abbr
9245         }
9246         if (this.align) {
9247             cfg.align=this.align
9248         }
9249         if (this.axis) {
9250             cfg.axis=this.axis
9251         }
9252         if (this.bgcolor) {
9253             cfg.bgcolor=this.bgcolor
9254         }
9255         if (this.charoff) {
9256             cfg.charoff=this.charoff
9257         }
9258         if (this.colspan) {
9259             cfg.colspan=this.colspan
9260         }
9261         if (this.headers) {
9262             cfg.headers=this.headers
9263         }
9264         if (this.height) {
9265             cfg.height=this.height
9266         }
9267         if (this.nowrap) {
9268             cfg.nowrap=this.nowrap
9269         }
9270         if (this.rowspan) {
9271             cfg.rowspan=this.rowspan
9272         }
9273         if (this.scope) {
9274             cfg.scope=this.scope
9275         }
9276         if (this.valign) {
9277             cfg.valign=this.valign
9278         }
9279         if (this.width) {
9280             cfg.width=this.width
9281         }
9282         
9283         
9284         return cfg;
9285     }
9286    
9287 });
9288
9289  
9290
9291  /*
9292  * - LGPL
9293  *
9294  * table row
9295  * 
9296  */
9297
9298 /**
9299  * @class Roo.bootstrap.TableRow
9300  * @extends Roo.bootstrap.Component
9301  * Bootstrap TableRow class
9302  * @cfg {String} cls row class
9303  * @cfg {String} align Aligns the content in a table row
9304  * @cfg {String} bgcolor Specifies a background color for a table row
9305  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9306  * @cfg {String} valign Vertical aligns the content in a table row
9307  * 
9308  * @constructor
9309  * Create a new TableRow
9310  * @param {Object} config The config object
9311  */
9312
9313 Roo.bootstrap.TableRow = function(config){
9314     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9315 };
9316
9317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9318     
9319     cls: false,
9320     align: false,
9321     bgcolor: false,
9322     charoff: false,
9323     valign: false,
9324     
9325     getAutoCreate : function(){
9326         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9327         
9328         cfg = {
9329             tag: 'tr'
9330         };
9331             
9332         if(this.cls){
9333             cfg.cls = this.cls;
9334         }
9335         if(this.align){
9336             cfg.align = this.align;
9337         }
9338         if(this.bgcolor){
9339             cfg.bgcolor = this.bgcolor;
9340         }
9341         if(this.charoff){
9342             cfg.charoff = this.charoff;
9343         }
9344         if(this.valign){
9345             cfg.valign = this.valign;
9346         }
9347         
9348         return cfg;
9349     }
9350    
9351 });
9352
9353  
9354
9355  /*
9356  * - LGPL
9357  *
9358  * table body
9359  * 
9360  */
9361
9362 /**
9363  * @class Roo.bootstrap.TableBody
9364  * @extends Roo.bootstrap.Component
9365  * Bootstrap TableBody class
9366  * @cfg {String} cls element class
9367  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9368  * @cfg {String} align Aligns the content inside the element
9369  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9370  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9371  * 
9372  * @constructor
9373  * Create a new TableBody
9374  * @param {Object} config The config object
9375  */
9376
9377 Roo.bootstrap.TableBody = function(config){
9378     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9379 };
9380
9381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9382     
9383     cls: false,
9384     tag: false,
9385     align: false,
9386     charoff: false,
9387     valign: false,
9388     
9389     getAutoCreate : function(){
9390         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9391         
9392         cfg = {
9393             tag: 'tbody'
9394         };
9395             
9396         if (this.cls) {
9397             cfg.cls=this.cls
9398         }
9399         if(this.tag){
9400             cfg.tag = this.tag;
9401         }
9402         
9403         if(this.align){
9404             cfg.align = this.align;
9405         }
9406         if(this.charoff){
9407             cfg.charoff = this.charoff;
9408         }
9409         if(this.valign){
9410             cfg.valign = this.valign;
9411         }
9412         
9413         return cfg;
9414     }
9415     
9416     
9417 //    initEvents : function()
9418 //    {
9419 //        
9420 //        if(!this.store){
9421 //            return;
9422 //        }
9423 //        
9424 //        this.store = Roo.factory(this.store, Roo.data);
9425 //        this.store.on('load', this.onLoad, this);
9426 //        
9427 //        this.store.load();
9428 //        
9429 //    },
9430 //    
9431 //    onLoad: function () 
9432 //    {   
9433 //        this.fireEvent('load', this);
9434 //    }
9435 //    
9436 //   
9437 });
9438
9439  
9440
9441  /*
9442  * Based on:
9443  * Ext JS Library 1.1.1
9444  * Copyright(c) 2006-2007, Ext JS, LLC.
9445  *
9446  * Originally Released Under LGPL - original licence link has changed is not relivant.
9447  *
9448  * Fork - LGPL
9449  * <script type="text/javascript">
9450  */
9451
9452 // as we use this in bootstrap.
9453 Roo.namespace('Roo.form');
9454  /**
9455  * @class Roo.form.Action
9456  * Internal Class used to handle form actions
9457  * @constructor
9458  * @param {Roo.form.BasicForm} el The form element or its id
9459  * @param {Object} config Configuration options
9460  */
9461
9462  
9463  
9464 // define the action interface
9465 Roo.form.Action = function(form, options){
9466     this.form = form;
9467     this.options = options || {};
9468 };
9469 /**
9470  * Client Validation Failed
9471  * @const 
9472  */
9473 Roo.form.Action.CLIENT_INVALID = 'client';
9474 /**
9475  * Server Validation Failed
9476  * @const 
9477  */
9478 Roo.form.Action.SERVER_INVALID = 'server';
9479  /**
9480  * Connect to Server Failed
9481  * @const 
9482  */
9483 Roo.form.Action.CONNECT_FAILURE = 'connect';
9484 /**
9485  * Reading Data from Server Failed
9486  * @const 
9487  */
9488 Roo.form.Action.LOAD_FAILURE = 'load';
9489
9490 Roo.form.Action.prototype = {
9491     type : 'default',
9492     failureType : undefined,
9493     response : undefined,
9494     result : undefined,
9495
9496     // interface method
9497     run : function(options){
9498
9499     },
9500
9501     // interface method
9502     success : function(response){
9503
9504     },
9505
9506     // interface method
9507     handleResponse : function(response){
9508
9509     },
9510
9511     // default connection failure
9512     failure : function(response){
9513         
9514         this.response = response;
9515         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9516         this.form.afterAction(this, false);
9517     },
9518
9519     processResponse : function(response){
9520         this.response = response;
9521         if(!response.responseText){
9522             return true;
9523         }
9524         this.result = this.handleResponse(response);
9525         return this.result;
9526     },
9527
9528     // utility functions used internally
9529     getUrl : function(appendParams){
9530         var url = this.options.url || this.form.url || this.form.el.dom.action;
9531         if(appendParams){
9532             var p = this.getParams();
9533             if(p){
9534                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9535             }
9536         }
9537         return url;
9538     },
9539
9540     getMethod : function(){
9541         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9542     },
9543
9544     getParams : function(){
9545         var bp = this.form.baseParams;
9546         var p = this.options.params;
9547         if(p){
9548             if(typeof p == "object"){
9549                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9550             }else if(typeof p == 'string' && bp){
9551                 p += '&' + Roo.urlEncode(bp);
9552             }
9553         }else if(bp){
9554             p = Roo.urlEncode(bp);
9555         }
9556         return p;
9557     },
9558
9559     createCallback : function(){
9560         return {
9561             success: this.success,
9562             failure: this.failure,
9563             scope: this,
9564             timeout: (this.form.timeout*1000),
9565             upload: this.form.fileUpload ? this.success : undefined
9566         };
9567     }
9568 };
9569
9570 Roo.form.Action.Submit = function(form, options){
9571     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9572 };
9573
9574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9575     type : 'submit',
9576
9577     haveProgress : false,
9578     uploadComplete : false,
9579     
9580     // uploadProgress indicator.
9581     uploadProgress : function()
9582     {
9583         if (!this.form.progressUrl) {
9584             return;
9585         }
9586         
9587         if (!this.haveProgress) {
9588             Roo.MessageBox.progress("Uploading", "Uploading");
9589         }
9590         if (this.uploadComplete) {
9591            Roo.MessageBox.hide();
9592            return;
9593         }
9594         
9595         this.haveProgress = true;
9596    
9597         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9598         
9599         var c = new Roo.data.Connection();
9600         c.request({
9601             url : this.form.progressUrl,
9602             params: {
9603                 id : uid
9604             },
9605             method: 'GET',
9606             success : function(req){
9607                //console.log(data);
9608                 var rdata = false;
9609                 var edata;
9610                 try  {
9611                    rdata = Roo.decode(req.responseText)
9612                 } catch (e) {
9613                     Roo.log("Invalid data from server..");
9614                     Roo.log(edata);
9615                     return;
9616                 }
9617                 if (!rdata || !rdata.success) {
9618                     Roo.log(rdata);
9619                     Roo.MessageBox.alert(Roo.encode(rdata));
9620                     return;
9621                 }
9622                 var data = rdata.data;
9623                 
9624                 if (this.uploadComplete) {
9625                    Roo.MessageBox.hide();
9626                    return;
9627                 }
9628                    
9629                 if (data){
9630                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9631                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9632                     );
9633                 }
9634                 this.uploadProgress.defer(2000,this);
9635             },
9636        
9637             failure: function(data) {
9638                 Roo.log('progress url failed ');
9639                 Roo.log(data);
9640             },
9641             scope : this
9642         });
9643            
9644     },
9645     
9646     
9647     run : function()
9648     {
9649         // run get Values on the form, so it syncs any secondary forms.
9650         this.form.getValues();
9651         
9652         var o = this.options;
9653         var method = this.getMethod();
9654         var isPost = method == 'POST';
9655         if(o.clientValidation === false || this.form.isValid()){
9656             
9657             if (this.form.progressUrl) {
9658                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9659                     (new Date() * 1) + '' + Math.random());
9660                     
9661             } 
9662             
9663             
9664             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9665                 form:this.form.el.dom,
9666                 url:this.getUrl(!isPost),
9667                 method: method,
9668                 params:isPost ? this.getParams() : null,
9669                 isUpload: this.form.fileUpload,
9670                 formData : this.form.formData
9671             }));
9672             
9673             this.uploadProgress();
9674
9675         }else if (o.clientValidation !== false){ // client validation failed
9676             this.failureType = Roo.form.Action.CLIENT_INVALID;
9677             this.form.afterAction(this, false);
9678         }
9679     },
9680
9681     success : function(response)
9682     {
9683         this.uploadComplete= true;
9684         if (this.haveProgress) {
9685             Roo.MessageBox.hide();
9686         }
9687         
9688         
9689         var result = this.processResponse(response);
9690         if(result === true || result.success){
9691             this.form.afterAction(this, true);
9692             return;
9693         }
9694         if(result.errors){
9695             this.form.markInvalid(result.errors);
9696             this.failureType = Roo.form.Action.SERVER_INVALID;
9697         }
9698         this.form.afterAction(this, false);
9699     },
9700     failure : function(response)
9701     {
9702         this.uploadComplete= true;
9703         if (this.haveProgress) {
9704             Roo.MessageBox.hide();
9705         }
9706         
9707         this.response = response;
9708         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9709         this.form.afterAction(this, false);
9710     },
9711     
9712     handleResponse : function(response){
9713         if(this.form.errorReader){
9714             var rs = this.form.errorReader.read(response);
9715             var errors = [];
9716             if(rs.records){
9717                 for(var i = 0, len = rs.records.length; i < len; i++) {
9718                     var r = rs.records[i];
9719                     errors[i] = r.data;
9720                 }
9721             }
9722             if(errors.length < 1){
9723                 errors = null;
9724             }
9725             return {
9726                 success : rs.success,
9727                 errors : errors
9728             };
9729         }
9730         var ret = false;
9731         try {
9732             ret = Roo.decode(response.responseText);
9733         } catch (e) {
9734             ret = {
9735                 success: false,
9736                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9737                 errors : []
9738             };
9739         }
9740         return ret;
9741         
9742     }
9743 });
9744
9745
9746 Roo.form.Action.Load = function(form, options){
9747     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9748     this.reader = this.form.reader;
9749 };
9750
9751 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9752     type : 'load',
9753
9754     run : function(){
9755         
9756         Roo.Ajax.request(Roo.apply(
9757                 this.createCallback(), {
9758                     method:this.getMethod(),
9759                     url:this.getUrl(false),
9760                     params:this.getParams()
9761         }));
9762     },
9763
9764     success : function(response){
9765         
9766         var result = this.processResponse(response);
9767         if(result === true || !result.success || !result.data){
9768             this.failureType = Roo.form.Action.LOAD_FAILURE;
9769             this.form.afterAction(this, false);
9770             return;
9771         }
9772         this.form.clearInvalid();
9773         this.form.setValues(result.data);
9774         this.form.afterAction(this, true);
9775     },
9776
9777     handleResponse : function(response){
9778         if(this.form.reader){
9779             var rs = this.form.reader.read(response);
9780             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9781             return {
9782                 success : rs.success,
9783                 data : data
9784             };
9785         }
9786         return Roo.decode(response.responseText);
9787     }
9788 });
9789
9790 Roo.form.Action.ACTION_TYPES = {
9791     'load' : Roo.form.Action.Load,
9792     'submit' : Roo.form.Action.Submit
9793 };/*
9794  * - LGPL
9795  *
9796  * form
9797  *
9798  */
9799
9800 /**
9801  * @class Roo.bootstrap.Form
9802  * @extends Roo.bootstrap.Component
9803  * Bootstrap Form class
9804  * @cfg {String} method  GET | POST (default POST)
9805  * @cfg {String} labelAlign top | left (default top)
9806  * @cfg {String} align left  | right - for navbars
9807  * @cfg {Boolean} loadMask load mask when submit (default true)
9808
9809  *
9810  * @constructor
9811  * Create a new Form
9812  * @param {Object} config The config object
9813  */
9814
9815
9816 Roo.bootstrap.Form = function(config){
9817     
9818     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9819     
9820     Roo.bootstrap.Form.popover.apply();
9821     
9822     this.addEvents({
9823         /**
9824          * @event clientvalidation
9825          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9826          * @param {Form} this
9827          * @param {Boolean} valid true if the form has passed client-side validation
9828          */
9829         clientvalidation: true,
9830         /**
9831          * @event beforeaction
9832          * Fires before any action is performed. Return false to cancel the action.
9833          * @param {Form} this
9834          * @param {Action} action The action to be performed
9835          */
9836         beforeaction: true,
9837         /**
9838          * @event actionfailed
9839          * Fires when an action fails.
9840          * @param {Form} this
9841          * @param {Action} action The action that failed
9842          */
9843         actionfailed : true,
9844         /**
9845          * @event actioncomplete
9846          * Fires when an action is completed.
9847          * @param {Form} this
9848          * @param {Action} action The action that completed
9849          */
9850         actioncomplete : true
9851     });
9852 };
9853
9854 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9855
9856      /**
9857      * @cfg {String} method
9858      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9859      */
9860     method : 'POST',
9861     /**
9862      * @cfg {String} url
9863      * The URL to use for form actions if one isn't supplied in the action options.
9864      */
9865     /**
9866      * @cfg {Boolean} fileUpload
9867      * Set to true if this form is a file upload.
9868      */
9869
9870     /**
9871      * @cfg {Object} baseParams
9872      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9873      */
9874
9875     /**
9876      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9877      */
9878     timeout: 30,
9879     /**
9880      * @cfg {Sting} align (left|right) for navbar forms
9881      */
9882     align : 'left',
9883
9884     // private
9885     activeAction : null,
9886
9887     /**
9888      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9889      * element by passing it or its id or mask the form itself by passing in true.
9890      * @type Mixed
9891      */
9892     waitMsgTarget : false,
9893
9894     loadMask : true,
9895     
9896     /**
9897      * @cfg {Boolean} errorMask (true|false) default false
9898      */
9899     errorMask : false,
9900     
9901     /**
9902      * @cfg {Number} maskOffset Default 100
9903      */
9904     maskOffset : 100,
9905     
9906     /**
9907      * @cfg {Boolean} maskBody
9908      */
9909     maskBody : false,
9910
9911     getAutoCreate : function(){
9912
9913         var cfg = {
9914             tag: 'form',
9915             method : this.method || 'POST',
9916             id : this.id || Roo.id(),
9917             cls : ''
9918         };
9919         if (this.parent().xtype.match(/^Nav/)) {
9920             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9921
9922         }
9923
9924         if (this.labelAlign == 'left' ) {
9925             cfg.cls += ' form-horizontal';
9926         }
9927
9928
9929         return cfg;
9930     },
9931     initEvents : function()
9932     {
9933         this.el.on('submit', this.onSubmit, this);
9934         // this was added as random key presses on the form where triggering form submit.
9935         this.el.on('keypress', function(e) {
9936             if (e.getCharCode() != 13) {
9937                 return true;
9938             }
9939             // we might need to allow it for textareas.. and some other items.
9940             // check e.getTarget().
9941
9942             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9943                 return true;
9944             }
9945
9946             Roo.log("keypress blocked");
9947
9948             e.preventDefault();
9949             return false;
9950         });
9951         
9952     },
9953     // private
9954     onSubmit : function(e){
9955         e.stopEvent();
9956     },
9957
9958      /**
9959      * Returns true if client-side validation on the form is successful.
9960      * @return Boolean
9961      */
9962     isValid : function(){
9963         var items = this.getItems();
9964         var valid = true;
9965         var target = false;
9966         
9967         items.each(function(f){
9968             
9969             if(f.validate()){
9970                 return;
9971             }
9972             
9973             Roo.log('invalid field: ' + f.name);
9974             
9975             valid = false;
9976
9977             if(!target && f.el.isVisible(true)){
9978                 target = f;
9979             }
9980            
9981         });
9982         
9983         if(this.errorMask && !valid){
9984             Roo.bootstrap.Form.popover.mask(this, target);
9985         }
9986         
9987         return valid;
9988     },
9989     
9990     /**
9991      * Returns true if any fields in this form have changed since their original load.
9992      * @return Boolean
9993      */
9994     isDirty : function(){
9995         var dirty = false;
9996         var items = this.getItems();
9997         items.each(function(f){
9998            if(f.isDirty()){
9999                dirty = true;
10000                return false;
10001            }
10002            return true;
10003         });
10004         return dirty;
10005     },
10006      /**
10007      * Performs a predefined action (submit or load) or custom actions you define on this form.
10008      * @param {String} actionName The name of the action type
10009      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10010      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10011      * accept other config options):
10012      * <pre>
10013 Property          Type             Description
10014 ----------------  ---------------  ----------------------------------------------------------------------------------
10015 url               String           The url for the action (defaults to the form's url)
10016 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10017 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10018 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10019                                    validate the form on the client (defaults to false)
10020      * </pre>
10021      * @return {BasicForm} this
10022      */
10023     doAction : function(action, options){
10024         if(typeof action == 'string'){
10025             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10026         }
10027         if(this.fireEvent('beforeaction', this, action) !== false){
10028             this.beforeAction(action);
10029             action.run.defer(100, action);
10030         }
10031         return this;
10032     },
10033
10034     // private
10035     beforeAction : function(action){
10036         var o = action.options;
10037         
10038         if(this.loadMask){
10039             
10040             if(this.maskBody){
10041                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10042             } else {
10043                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10044             }
10045         }
10046         // not really supported yet.. ??
10047
10048         //if(this.waitMsgTarget === true){
10049         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10050         //}else if(this.waitMsgTarget){
10051         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10052         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10053         //}else {
10054         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10055        // }
10056
10057     },
10058
10059     // private
10060     afterAction : function(action, success){
10061         this.activeAction = null;
10062         var o = action.options;
10063
10064         if(this.loadMask){
10065             
10066             if(this.maskBody){
10067                 Roo.get(document.body).unmask();
10068             } else {
10069                 this.el.unmask();
10070             }
10071         }
10072         
10073         //if(this.waitMsgTarget === true){
10074 //            this.el.unmask();
10075         //}else if(this.waitMsgTarget){
10076         //    this.waitMsgTarget.unmask();
10077         //}else{
10078         //    Roo.MessageBox.updateProgress(1);
10079         //    Roo.MessageBox.hide();
10080        // }
10081         //
10082         if(success){
10083             if(o.reset){
10084                 this.reset();
10085             }
10086             Roo.callback(o.success, o.scope, [this, action]);
10087             this.fireEvent('actioncomplete', this, action);
10088
10089         }else{
10090
10091             // failure condition..
10092             // we have a scenario where updates need confirming.
10093             // eg. if a locking scenario exists..
10094             // we look for { errors : { needs_confirm : true }} in the response.
10095             if (
10096                 (typeof(action.result) != 'undefined')  &&
10097                 (typeof(action.result.errors) != 'undefined')  &&
10098                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10099            ){
10100                 var _t = this;
10101                 Roo.log("not supported yet");
10102                  /*
10103
10104                 Roo.MessageBox.confirm(
10105                     "Change requires confirmation",
10106                     action.result.errorMsg,
10107                     function(r) {
10108                         if (r != 'yes') {
10109                             return;
10110                         }
10111                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10112                     }
10113
10114                 );
10115                 */
10116
10117
10118                 return;
10119             }
10120
10121             Roo.callback(o.failure, o.scope, [this, action]);
10122             // show an error message if no failed handler is set..
10123             if (!this.hasListener('actionfailed')) {
10124                 Roo.log("need to add dialog support");
10125                 /*
10126                 Roo.MessageBox.alert("Error",
10127                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10128                         action.result.errorMsg :
10129                         "Saving Failed, please check your entries or try again"
10130                 );
10131                 */
10132             }
10133
10134             this.fireEvent('actionfailed', this, action);
10135         }
10136
10137     },
10138     /**
10139      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10140      * @param {String} id The value to search for
10141      * @return Field
10142      */
10143     findField : function(id){
10144         var items = this.getItems();
10145         var field = items.get(id);
10146         if(!field){
10147              items.each(function(f){
10148                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10149                     field = f;
10150                     return false;
10151                 }
10152                 return true;
10153             });
10154         }
10155         return field || null;
10156     },
10157      /**
10158      * Mark fields in this form invalid in bulk.
10159      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10160      * @return {BasicForm} this
10161      */
10162     markInvalid : function(errors){
10163         if(errors instanceof Array){
10164             for(var i = 0, len = errors.length; i < len; i++){
10165                 var fieldError = errors[i];
10166                 var f = this.findField(fieldError.id);
10167                 if(f){
10168                     f.markInvalid(fieldError.msg);
10169                 }
10170             }
10171         }else{
10172             var field, id;
10173             for(id in errors){
10174                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10175                     field.markInvalid(errors[id]);
10176                 }
10177             }
10178         }
10179         //Roo.each(this.childForms || [], function (f) {
10180         //    f.markInvalid(errors);
10181         //});
10182
10183         return this;
10184     },
10185
10186     /**
10187      * Set values for fields in this form in bulk.
10188      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10189      * @return {BasicForm} this
10190      */
10191     setValues : function(values){
10192         if(values instanceof Array){ // array of objects
10193             for(var i = 0, len = values.length; i < len; i++){
10194                 var v = values[i];
10195                 var f = this.findField(v.id);
10196                 if(f){
10197                     f.setValue(v.value);
10198                     if(this.trackResetOnLoad){
10199                         f.originalValue = f.getValue();
10200                     }
10201                 }
10202             }
10203         }else{ // object hash
10204             var field, id;
10205             for(id in values){
10206                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10207
10208                     if (field.setFromData &&
10209                         field.valueField &&
10210                         field.displayField &&
10211                         // combos' with local stores can
10212                         // be queried via setValue()
10213                         // to set their value..
10214                         (field.store && !field.store.isLocal)
10215                         ) {
10216                         // it's a combo
10217                         var sd = { };
10218                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10219                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10220                         field.setFromData(sd);
10221
10222                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10223                         
10224                         field.setFromData(values);
10225                         
10226                     } else {
10227                         field.setValue(values[id]);
10228                     }
10229
10230
10231                     if(this.trackResetOnLoad){
10232                         field.originalValue = field.getValue();
10233                     }
10234                 }
10235             }
10236         }
10237
10238         //Roo.each(this.childForms || [], function (f) {
10239         //    f.setValues(values);
10240         //});
10241
10242         return this;
10243     },
10244
10245     /**
10246      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10247      * they are returned as an array.
10248      * @param {Boolean} asString
10249      * @return {Object}
10250      */
10251     getValues : function(asString){
10252         //if (this.childForms) {
10253             // copy values from the child forms
10254         //    Roo.each(this.childForms, function (f) {
10255         //        this.setValues(f.getValues());
10256         //    }, this);
10257         //}
10258
10259
10260
10261         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10262         if(asString === true){
10263             return fs;
10264         }
10265         return Roo.urlDecode(fs);
10266     },
10267
10268     /**
10269      * Returns the fields in this form as an object with key/value pairs.
10270      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10271      * @return {Object}
10272      */
10273     getFieldValues : function(with_hidden)
10274     {
10275         var items = this.getItems();
10276         var ret = {};
10277         items.each(function(f){
10278             
10279             if (!f.getName()) {
10280                 return;
10281             }
10282             
10283             var v = f.getValue();
10284             
10285             if (f.inputType =='radio') {
10286                 if (typeof(ret[f.getName()]) == 'undefined') {
10287                     ret[f.getName()] = ''; // empty..
10288                 }
10289
10290                 if (!f.el.dom.checked) {
10291                     return;
10292
10293                 }
10294                 v = f.el.dom.value;
10295
10296             }
10297             
10298             if(f.xtype == 'MoneyField'){
10299                 ret[f.currencyName] = f.getCurrency();
10300             }
10301
10302             // not sure if this supported any more..
10303             if ((typeof(v) == 'object') && f.getRawValue) {
10304                 v = f.getRawValue() ; // dates..
10305             }
10306             // combo boxes where name != hiddenName...
10307             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10308                 ret[f.name] = f.getRawValue();
10309             }
10310             ret[f.getName()] = v;
10311         });
10312
10313         return ret;
10314     },
10315
10316     /**
10317      * Clears all invalid messages in this form.
10318      * @return {BasicForm} this
10319      */
10320     clearInvalid : function(){
10321         var items = this.getItems();
10322
10323         items.each(function(f){
10324            f.clearInvalid();
10325         });
10326
10327         return this;
10328     },
10329
10330     /**
10331      * Resets this form.
10332      * @return {BasicForm} this
10333      */
10334     reset : function(){
10335         var items = this.getItems();
10336         items.each(function(f){
10337             f.reset();
10338         });
10339
10340         Roo.each(this.childForms || [], function (f) {
10341             f.reset();
10342         });
10343
10344
10345         return this;
10346     },
10347     
10348     getItems : function()
10349     {
10350         var r=new Roo.util.MixedCollection(false, function(o){
10351             return o.id || (o.id = Roo.id());
10352         });
10353         var iter = function(el) {
10354             if (el.inputEl) {
10355                 r.add(el);
10356             }
10357             if (!el.items) {
10358                 return;
10359             }
10360             Roo.each(el.items,function(e) {
10361                 iter(e);
10362             });
10363         };
10364
10365         iter(this);
10366         return r;
10367     },
10368     
10369     hideFields : function(items)
10370     {
10371         Roo.each(items, function(i){
10372             
10373             var f = this.findField(i);
10374             
10375             if(!f){
10376                 return;
10377             }
10378             
10379             f.hide();
10380             
10381         }, this);
10382     },
10383     
10384     showFields : function(items)
10385     {
10386         Roo.each(items, function(i){
10387             
10388             var f = this.findField(i);
10389             
10390             if(!f){
10391                 return;
10392             }
10393             
10394             f.show();
10395             
10396         }, this);
10397     }
10398
10399 });
10400
10401 Roo.apply(Roo.bootstrap.Form, {
10402     
10403     popover : {
10404         
10405         padding : 5,
10406         
10407         isApplied : false,
10408         
10409         isMasked : false,
10410         
10411         form : false,
10412         
10413         target : false,
10414         
10415         toolTip : false,
10416         
10417         intervalID : false,
10418         
10419         maskEl : false,
10420         
10421         apply : function()
10422         {
10423             if(this.isApplied){
10424                 return;
10425             }
10426             
10427             this.maskEl = {
10428                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10429                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10430                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10431                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10432             };
10433             
10434             this.maskEl.top.enableDisplayMode("block");
10435             this.maskEl.left.enableDisplayMode("block");
10436             this.maskEl.bottom.enableDisplayMode("block");
10437             this.maskEl.right.enableDisplayMode("block");
10438             
10439             this.toolTip = new Roo.bootstrap.Tooltip({
10440                 cls : 'roo-form-error-popover',
10441                 alignment : {
10442                     'left' : ['r-l', [-2,0], 'right'],
10443                     'right' : ['l-r', [2,0], 'left'],
10444                     'bottom' : ['tl-bl', [0,2], 'top'],
10445                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10446                 }
10447             });
10448             
10449             this.toolTip.render(Roo.get(document.body));
10450
10451             this.toolTip.el.enableDisplayMode("block");
10452             
10453             Roo.get(document.body).on('click', function(){
10454                 this.unmask();
10455             }, this);
10456             
10457             Roo.get(document.body).on('touchstart', function(){
10458                 this.unmask();
10459             }, this);
10460             
10461             this.isApplied = true
10462         },
10463         
10464         mask : function(form, target)
10465         {
10466             this.form = form;
10467             
10468             this.target = target;
10469             
10470             if(!this.form.errorMask || !target.el){
10471                 return;
10472             }
10473             
10474             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10475             
10476             Roo.log(scrollable);
10477             
10478             var ot = this.target.el.calcOffsetsTo(scrollable);
10479             
10480             var scrollTo = ot[1] - this.form.maskOffset;
10481             
10482             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10483             
10484             scrollable.scrollTo('top', scrollTo);
10485             
10486             var box = this.target.el.getBox();
10487             Roo.log(box);
10488             var zIndex = Roo.bootstrap.Modal.zIndex++;
10489
10490             
10491             this.maskEl.top.setStyle('position', 'absolute');
10492             this.maskEl.top.setStyle('z-index', zIndex);
10493             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10494             this.maskEl.top.setLeft(0);
10495             this.maskEl.top.setTop(0);
10496             this.maskEl.top.show();
10497             
10498             this.maskEl.left.setStyle('position', 'absolute');
10499             this.maskEl.left.setStyle('z-index', zIndex);
10500             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10501             this.maskEl.left.setLeft(0);
10502             this.maskEl.left.setTop(box.y - this.padding);
10503             this.maskEl.left.show();
10504
10505             this.maskEl.bottom.setStyle('position', 'absolute');
10506             this.maskEl.bottom.setStyle('z-index', zIndex);
10507             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10508             this.maskEl.bottom.setLeft(0);
10509             this.maskEl.bottom.setTop(box.bottom + this.padding);
10510             this.maskEl.bottom.show();
10511
10512             this.maskEl.right.setStyle('position', 'absolute');
10513             this.maskEl.right.setStyle('z-index', zIndex);
10514             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10515             this.maskEl.right.setLeft(box.right + this.padding);
10516             this.maskEl.right.setTop(box.y - this.padding);
10517             this.maskEl.right.show();
10518
10519             this.toolTip.bindEl = this.target.el;
10520
10521             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10522
10523             var tip = this.target.blankText;
10524
10525             if(this.target.getValue() !== '' ) {
10526                 
10527                 if (this.target.invalidText.length) {
10528                     tip = this.target.invalidText;
10529                 } else if (this.target.regexText.length){
10530                     tip = this.target.regexText;
10531                 }
10532             }
10533
10534             this.toolTip.show(tip);
10535
10536             this.intervalID = window.setInterval(function() {
10537                 Roo.bootstrap.Form.popover.unmask();
10538             }, 10000);
10539
10540             window.onwheel = function(){ return false;};
10541             
10542             (function(){ this.isMasked = true; }).defer(500, this);
10543             
10544         },
10545         
10546         unmask : function()
10547         {
10548             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10549                 return;
10550             }
10551             
10552             this.maskEl.top.setStyle('position', 'absolute');
10553             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10554             this.maskEl.top.hide();
10555
10556             this.maskEl.left.setStyle('position', 'absolute');
10557             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10558             this.maskEl.left.hide();
10559
10560             this.maskEl.bottom.setStyle('position', 'absolute');
10561             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10562             this.maskEl.bottom.hide();
10563
10564             this.maskEl.right.setStyle('position', 'absolute');
10565             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10566             this.maskEl.right.hide();
10567             
10568             this.toolTip.hide();
10569             
10570             this.toolTip.el.hide();
10571             
10572             window.onwheel = function(){ return true;};
10573             
10574             if(this.intervalID){
10575                 window.clearInterval(this.intervalID);
10576                 this.intervalID = false;
10577             }
10578             
10579             this.isMasked = false;
10580             
10581         }
10582         
10583     }
10584     
10585 });
10586
10587 /*
10588  * Based on:
10589  * Ext JS Library 1.1.1
10590  * Copyright(c) 2006-2007, Ext JS, LLC.
10591  *
10592  * Originally Released Under LGPL - original licence link has changed is not relivant.
10593  *
10594  * Fork - LGPL
10595  * <script type="text/javascript">
10596  */
10597 /**
10598  * @class Roo.form.VTypes
10599  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10600  * @singleton
10601  */
10602 Roo.form.VTypes = function(){
10603     // closure these in so they are only created once.
10604     var alpha = /^[a-zA-Z_]+$/;
10605     var alphanum = /^[a-zA-Z0-9_]+$/;
10606     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10607     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10608
10609     // All these messages and functions are configurable
10610     return {
10611         /**
10612          * The function used to validate email addresses
10613          * @param {String} value The email address
10614          */
10615         'email' : function(v){
10616             return email.test(v);
10617         },
10618         /**
10619          * The error text to display when the email validation function returns false
10620          * @type String
10621          */
10622         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10623         /**
10624          * The keystroke filter mask to be applied on email input
10625          * @type RegExp
10626          */
10627         'emailMask' : /[a-z0-9_\.\-@]/i,
10628
10629         /**
10630          * The function used to validate URLs
10631          * @param {String} value The URL
10632          */
10633         'url' : function(v){
10634             return url.test(v);
10635         },
10636         /**
10637          * The error text to display when the url validation function returns false
10638          * @type String
10639          */
10640         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10641         
10642         /**
10643          * The function used to validate alpha values
10644          * @param {String} value The value
10645          */
10646         'alpha' : function(v){
10647             return alpha.test(v);
10648         },
10649         /**
10650          * The error text to display when the alpha validation function returns false
10651          * @type String
10652          */
10653         'alphaText' : 'This field should only contain letters and _',
10654         /**
10655          * The keystroke filter mask to be applied on alpha input
10656          * @type RegExp
10657          */
10658         'alphaMask' : /[a-z_]/i,
10659
10660         /**
10661          * The function used to validate alphanumeric values
10662          * @param {String} value The value
10663          */
10664         'alphanum' : function(v){
10665             return alphanum.test(v);
10666         },
10667         /**
10668          * The error text to display when the alphanumeric validation function returns false
10669          * @type String
10670          */
10671         'alphanumText' : 'This field should only contain letters, numbers and _',
10672         /**
10673          * The keystroke filter mask to be applied on alphanumeric input
10674          * @type RegExp
10675          */
10676         'alphanumMask' : /[a-z0-9_]/i
10677     };
10678 }();/*
10679  * - LGPL
10680  *
10681  * Input
10682  * 
10683  */
10684
10685 /**
10686  * @class Roo.bootstrap.Input
10687  * @extends Roo.bootstrap.Component
10688  * Bootstrap Input class
10689  * @cfg {Boolean} disabled is it disabled
10690  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10691  * @cfg {String} name name of the input
10692  * @cfg {string} fieldLabel - the label associated
10693  * @cfg {string} placeholder - placeholder to put in text.
10694  * @cfg {string}  before - input group add on before
10695  * @cfg {string} after - input group add on after
10696  * @cfg {string} size - (lg|sm) or leave empty..
10697  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10698  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10699  * @cfg {Number} md colspan out of 12 for computer-sized screens
10700  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10701  * @cfg {string} value default value of the input
10702  * @cfg {Number} labelWidth set the width of label 
10703  * @cfg {Number} labellg set the width of label (1-12)
10704  * @cfg {Number} labelmd set the width of label (1-12)
10705  * @cfg {Number} labelsm set the width of label (1-12)
10706  * @cfg {Number} labelxs set the width of label (1-12)
10707  * @cfg {String} labelAlign (top|left)
10708  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10709  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10710  * @cfg {String} indicatorpos (left|right) default left
10711  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10712  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10713  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10714
10715  * @cfg {String} align (left|center|right) Default left
10716  * @cfg {Boolean} forceFeedback (true|false) Default false
10717  * 
10718  * @constructor
10719  * Create a new Input
10720  * @param {Object} config The config object
10721  */
10722
10723 Roo.bootstrap.Input = function(config){
10724     
10725     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10726     
10727     this.addEvents({
10728         /**
10729          * @event focus
10730          * Fires when this field receives input focus.
10731          * @param {Roo.form.Field} this
10732          */
10733         focus : true,
10734         /**
10735          * @event blur
10736          * Fires when this field loses input focus.
10737          * @param {Roo.form.Field} this
10738          */
10739         blur : true,
10740         /**
10741          * @event specialkey
10742          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10743          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10744          * @param {Roo.form.Field} this
10745          * @param {Roo.EventObject} e The event object
10746          */
10747         specialkey : true,
10748         /**
10749          * @event change
10750          * Fires just before the field blurs if the field value has changed.
10751          * @param {Roo.form.Field} this
10752          * @param {Mixed} newValue The new value
10753          * @param {Mixed} oldValue The original value
10754          */
10755         change : true,
10756         /**
10757          * @event invalid
10758          * Fires after the field has been marked as invalid.
10759          * @param {Roo.form.Field} this
10760          * @param {String} msg The validation message
10761          */
10762         invalid : true,
10763         /**
10764          * @event valid
10765          * Fires after the field has been validated with no errors.
10766          * @param {Roo.form.Field} this
10767          */
10768         valid : true,
10769          /**
10770          * @event keyup
10771          * Fires after the key up
10772          * @param {Roo.form.Field} this
10773          * @param {Roo.EventObject}  e The event Object
10774          */
10775         keyup : true,
10776         /**
10777          * @event paste
10778          * Fires after the user pastes into input
10779          * @param {Roo.form.Field} this
10780          * @param {Roo.EventObject}  e The event Object
10781          */
10782         paste : true
10783     });
10784 };
10785
10786 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10787      /**
10788      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10789       automatic validation (defaults to "keyup").
10790      */
10791     validationEvent : "keyup",
10792      /**
10793      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10794      */
10795     validateOnBlur : true,
10796     /**
10797      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10798      */
10799     validationDelay : 250,
10800      /**
10801      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10802      */
10803     focusClass : "x-form-focus",  // not needed???
10804     
10805        
10806     /**
10807      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10808      */
10809     invalidClass : "has-warning",
10810     
10811     /**
10812      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10813      */
10814     validClass : "has-success",
10815     
10816     /**
10817      * @cfg {Boolean} hasFeedback (true|false) default true
10818      */
10819     hasFeedback : true,
10820     
10821     /**
10822      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10823      */
10824     invalidFeedbackClass : "glyphicon-warning-sign",
10825     
10826     /**
10827      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10828      */
10829     validFeedbackClass : "glyphicon-ok",
10830     
10831     /**
10832      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10833      */
10834     selectOnFocus : false,
10835     
10836      /**
10837      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10838      */
10839     maskRe : null,
10840        /**
10841      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10842      */
10843     vtype : null,
10844     
10845       /**
10846      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10847      */
10848     disableKeyFilter : false,
10849     
10850        /**
10851      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10852      */
10853     disabled : false,
10854      /**
10855      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10856      */
10857     allowBlank : true,
10858     /**
10859      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10860      */
10861     blankText : "Please complete this mandatory field",
10862     
10863      /**
10864      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10865      */
10866     minLength : 0,
10867     /**
10868      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10869      */
10870     maxLength : Number.MAX_VALUE,
10871     /**
10872      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10873      */
10874     minLengthText : "The minimum length for this field is {0}",
10875     /**
10876      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10877      */
10878     maxLengthText : "The maximum length for this field is {0}",
10879   
10880     
10881     /**
10882      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10883      * If available, this function will be called only after the basic validators all return true, and will be passed the
10884      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10885      */
10886     validator : null,
10887     /**
10888      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10889      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10890      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10891      */
10892     regex : null,
10893     /**
10894      * @cfg {String} regexText -- Depricated - use Invalid Text
10895      */
10896     regexText : "",
10897     
10898     /**
10899      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10900      */
10901     invalidText : "",
10902     
10903     
10904     
10905     autocomplete: false,
10906     
10907     
10908     fieldLabel : '',
10909     inputType : 'text',
10910     
10911     name : false,
10912     placeholder: false,
10913     before : false,
10914     after : false,
10915     size : false,
10916     hasFocus : false,
10917     preventMark: false,
10918     isFormField : true,
10919     value : '',
10920     labelWidth : 2,
10921     labelAlign : false,
10922     readOnly : false,
10923     align : false,
10924     formatedValue : false,
10925     forceFeedback : false,
10926     
10927     indicatorpos : 'left',
10928     
10929     labellg : 0,
10930     labelmd : 0,
10931     labelsm : 0,
10932     labelxs : 0,
10933     
10934     capture : '',
10935     accept : '',
10936     
10937     parentLabelAlign : function()
10938     {
10939         var parent = this;
10940         while (parent.parent()) {
10941             parent = parent.parent();
10942             if (typeof(parent.labelAlign) !='undefined') {
10943                 return parent.labelAlign;
10944             }
10945         }
10946         return 'left';
10947         
10948     },
10949     
10950     getAutoCreate : function()
10951     {
10952         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10953         
10954         var id = Roo.id();
10955         
10956         var cfg = {};
10957         
10958         if(this.inputType != 'hidden'){
10959             cfg.cls = 'form-group' //input-group
10960         }
10961         
10962         var input =  {
10963             tag: 'input',
10964             id : id,
10965             type : this.inputType,
10966             value : this.value,
10967             cls : 'form-control',
10968             placeholder : this.placeholder || '',
10969             autocomplete : this.autocomplete || 'new-password'
10970         };
10971         if (this.inputType == 'file') {
10972             input.style = 'overflow:hidden'; // why not in CSS?
10973         }
10974         
10975         if(this.capture.length){
10976             input.capture = this.capture;
10977         }
10978         
10979         if(this.accept.length){
10980             input.accept = this.accept + "/*";
10981         }
10982         
10983         if(this.align){
10984             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10985         }
10986         
10987         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10988             input.maxLength = this.maxLength;
10989         }
10990         
10991         if (this.disabled) {
10992             input.disabled=true;
10993         }
10994         
10995         if (this.readOnly) {
10996             input.readonly=true;
10997         }
10998         
10999         if (this.name) {
11000             input.name = this.name;
11001         }
11002         
11003         if (this.size) {
11004             input.cls += ' input-' + this.size;
11005         }
11006         
11007         var settings=this;
11008         ['xs','sm','md','lg'].map(function(size){
11009             if (settings[size]) {
11010                 cfg.cls += ' col-' + size + '-' + settings[size];
11011             }
11012         });
11013         
11014         var inputblock = input;
11015         
11016         var feedback = {
11017             tag: 'span',
11018             cls: 'glyphicon form-control-feedback'
11019         };
11020             
11021         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11022             
11023             inputblock = {
11024                 cls : 'has-feedback',
11025                 cn :  [
11026                     input,
11027                     feedback
11028                 ] 
11029             };  
11030         }
11031         
11032         if (this.before || this.after) {
11033             
11034             inputblock = {
11035                 cls : 'input-group',
11036                 cn :  [] 
11037             };
11038             
11039             if (this.before && typeof(this.before) == 'string') {
11040                 
11041                 inputblock.cn.push({
11042                     tag :'span',
11043                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11044                     html : this.before
11045                 });
11046             }
11047             if (this.before && typeof(this.before) == 'object') {
11048                 this.before = Roo.factory(this.before);
11049                 
11050                 inputblock.cn.push({
11051                     tag :'span',
11052                     cls : 'roo-input-before input-group-prepend   input-group-' +
11053                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11054                 });
11055             }
11056             
11057             inputblock.cn.push(input);
11058             
11059             if (this.after && typeof(this.after) == 'string') {
11060                 inputblock.cn.push({
11061                     tag :'span',
11062                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11063                     html : this.after
11064                 });
11065             }
11066             if (this.after && typeof(this.after) == 'object') {
11067                 this.after = Roo.factory(this.after);
11068                 
11069                 inputblock.cn.push({
11070                     tag :'span',
11071                     cls : 'roo-input-after input-group-append  input-group-' +
11072                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11073                 });
11074             }
11075             
11076             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077                 inputblock.cls += ' has-feedback';
11078                 inputblock.cn.push(feedback);
11079             }
11080         };
11081         var indicator = {
11082             tag : 'i',
11083             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11084             tooltip : 'This field is required'
11085         };
11086         if (this.allowBlank ) {
11087             indicator.style = this.allowBlank ? ' display:none' : '';
11088         }
11089         if (align ==='left' && this.fieldLabel.length) {
11090             
11091             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11092             
11093             cfg.cn = [
11094                 indicator,
11095                 {
11096                     tag: 'label',
11097                     'for' :  id,
11098                     cls : 'control-label col-form-label',
11099                     html : this.fieldLabel
11100
11101                 },
11102                 {
11103                     cls : "", 
11104                     cn: [
11105                         inputblock
11106                     ]
11107                 }
11108             ];
11109             
11110             var labelCfg = cfg.cn[1];
11111             var contentCfg = cfg.cn[2];
11112             
11113             if(this.indicatorpos == 'right'){
11114                 cfg.cn = [
11115                     {
11116                         tag: 'label',
11117                         'for' :  id,
11118                         cls : 'control-label col-form-label',
11119                         cn : [
11120                             {
11121                                 tag : 'span',
11122                                 html : this.fieldLabel
11123                             },
11124                             indicator
11125                         ]
11126                     },
11127                     {
11128                         cls : "",
11129                         cn: [
11130                             inputblock
11131                         ]
11132                     }
11133
11134                 ];
11135                 
11136                 labelCfg = cfg.cn[0];
11137                 contentCfg = cfg.cn[1];
11138             
11139             }
11140             
11141             if(this.labelWidth > 12){
11142                 labelCfg.style = "width: " + this.labelWidth + 'px';
11143             }
11144             
11145             if(this.labelWidth < 13 && this.labelmd == 0){
11146                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11147             }
11148             
11149             if(this.labellg > 0){
11150                 labelCfg.cls += ' col-lg-' + this.labellg;
11151                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11152             }
11153             
11154             if(this.labelmd > 0){
11155                 labelCfg.cls += ' col-md-' + this.labelmd;
11156                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11157             }
11158             
11159             if(this.labelsm > 0){
11160                 labelCfg.cls += ' col-sm-' + this.labelsm;
11161                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11162             }
11163             
11164             if(this.labelxs > 0){
11165                 labelCfg.cls += ' col-xs-' + this.labelxs;
11166                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11167             }
11168             
11169             
11170         } else if ( this.fieldLabel.length) {
11171                 
11172             
11173             
11174             cfg.cn = [
11175                 {
11176                     tag : 'i',
11177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11178                     tooltip : 'This field is required',
11179                     style : this.allowBlank ? ' display:none' : '' 
11180                 },
11181                 {
11182                     tag: 'label',
11183                    //cls : 'input-group-addon',
11184                     html : this.fieldLabel
11185
11186                 },
11187
11188                inputblock
11189
11190            ];
11191            
11192            if(this.indicatorpos == 'right'){
11193        
11194                 cfg.cn = [
11195                     {
11196                         tag: 'label',
11197                        //cls : 'input-group-addon',
11198                         html : this.fieldLabel
11199
11200                     },
11201                     {
11202                         tag : 'i',
11203                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11204                         tooltip : 'This field is required',
11205                         style : this.allowBlank ? ' display:none' : '' 
11206                     },
11207
11208                    inputblock
11209
11210                ];
11211
11212             }
11213
11214         } else {
11215             
11216             cfg.cn = [
11217
11218                     inputblock
11219
11220             ];
11221                 
11222                 
11223         };
11224         
11225         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11226            cfg.cls += ' navbar-form';
11227         }
11228         
11229         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11230             // on BS4 we do this only if not form 
11231             cfg.cls += ' navbar-form';
11232             cfg.tag = 'li';
11233         }
11234         
11235         return cfg;
11236         
11237     },
11238     /**
11239      * return the real input element.
11240      */
11241     inputEl: function ()
11242     {
11243         return this.el.select('input.form-control',true).first();
11244     },
11245     
11246     tooltipEl : function()
11247     {
11248         return this.inputEl();
11249     },
11250     
11251     indicatorEl : function()
11252     {
11253         if (Roo.bootstrap.version == 4) {
11254             return false; // not enabled in v4 yet.
11255         }
11256         
11257         var indicator = this.el.select('i.roo-required-indicator',true).first();
11258         
11259         if(!indicator){
11260             return false;
11261         }
11262         
11263         return indicator;
11264         
11265     },
11266     
11267     setDisabled : function(v)
11268     {
11269         var i  = this.inputEl().dom;
11270         if (!v) {
11271             i.removeAttribute('disabled');
11272             return;
11273             
11274         }
11275         i.setAttribute('disabled','true');
11276     },
11277     initEvents : function()
11278     {
11279           
11280         this.inputEl().on("keydown" , this.fireKey,  this);
11281         this.inputEl().on("focus", this.onFocus,  this);
11282         this.inputEl().on("blur", this.onBlur,  this);
11283         
11284         this.inputEl().relayEvent('keyup', this);
11285         this.inputEl().relayEvent('paste', this);
11286         
11287         this.indicator = this.indicatorEl();
11288         
11289         if(this.indicator){
11290             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11291         }
11292  
11293         // reference to original value for reset
11294         this.originalValue = this.getValue();
11295         //Roo.form.TextField.superclass.initEvents.call(this);
11296         if(this.validationEvent == 'keyup'){
11297             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11298             this.inputEl().on('keyup', this.filterValidation, this);
11299         }
11300         else if(this.validationEvent !== false){
11301             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11302         }
11303         
11304         if(this.selectOnFocus){
11305             this.on("focus", this.preFocus, this);
11306             
11307         }
11308         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11309             this.inputEl().on("keypress", this.filterKeys, this);
11310         } else {
11311             this.inputEl().relayEvent('keypress', this);
11312         }
11313        /* if(this.grow){
11314             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11315             this.el.on("click", this.autoSize,  this);
11316         }
11317         */
11318         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11319             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11320         }
11321         
11322         if (typeof(this.before) == 'object') {
11323             this.before.render(this.el.select('.roo-input-before',true).first());
11324         }
11325         if (typeof(this.after) == 'object') {
11326             this.after.render(this.el.select('.roo-input-after',true).first());
11327         }
11328         
11329         this.inputEl().on('change', this.onChange, this);
11330         
11331     },
11332     filterValidation : function(e){
11333         if(!e.isNavKeyPress()){
11334             this.validationTask.delay(this.validationDelay);
11335         }
11336     },
11337      /**
11338      * Validates the field value
11339      * @return {Boolean} True if the value is valid, else false
11340      */
11341     validate : function(){
11342         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11343         if(this.disabled || this.validateValue(this.getRawValue())){
11344             this.markValid();
11345             return true;
11346         }
11347         
11348         this.markInvalid();
11349         return false;
11350     },
11351     
11352     
11353     /**
11354      * Validates a value according to the field's validation rules and marks the field as invalid
11355      * if the validation fails
11356      * @param {Mixed} value The value to validate
11357      * @return {Boolean} True if the value is valid, else false
11358      */
11359     validateValue : function(value)
11360     {
11361         if(this.getVisibilityEl().hasClass('hidden')){
11362             return true;
11363         }
11364         
11365         if(value.length < 1)  { // if it's blank
11366             if(this.allowBlank){
11367                 return true;
11368             }
11369             return false;
11370         }
11371         
11372         if(value.length < this.minLength){
11373             return false;
11374         }
11375         if(value.length > this.maxLength){
11376             return false;
11377         }
11378         if(this.vtype){
11379             var vt = Roo.form.VTypes;
11380             if(!vt[this.vtype](value, this)){
11381                 return false;
11382             }
11383         }
11384         if(typeof this.validator == "function"){
11385             var msg = this.validator(value);
11386             if(msg !== true){
11387                 return false;
11388             }
11389             if (typeof(msg) == 'string') {
11390                 this.invalidText = msg;
11391             }
11392         }
11393         
11394         if(this.regex && !this.regex.test(value)){
11395             return false;
11396         }
11397         
11398         return true;
11399     },
11400     
11401      // private
11402     fireKey : function(e){
11403         //Roo.log('field ' + e.getKey());
11404         if(e.isNavKeyPress()){
11405             this.fireEvent("specialkey", this, e);
11406         }
11407     },
11408     focus : function (selectText){
11409         if(this.rendered){
11410             this.inputEl().focus();
11411             if(selectText === true){
11412                 this.inputEl().dom.select();
11413             }
11414         }
11415         return this;
11416     } ,
11417     
11418     onFocus : function(){
11419         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11420            // this.el.addClass(this.focusClass);
11421         }
11422         if(!this.hasFocus){
11423             this.hasFocus = true;
11424             this.startValue = this.getValue();
11425             this.fireEvent("focus", this);
11426         }
11427     },
11428     
11429     beforeBlur : Roo.emptyFn,
11430
11431     
11432     // private
11433     onBlur : function(){
11434         this.beforeBlur();
11435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436             //this.el.removeClass(this.focusClass);
11437         }
11438         this.hasFocus = false;
11439         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11440             this.validate();
11441         }
11442         var v = this.getValue();
11443         if(String(v) !== String(this.startValue)){
11444             this.fireEvent('change', this, v, this.startValue);
11445         }
11446         this.fireEvent("blur", this);
11447     },
11448     
11449     onChange : function(e)
11450     {
11451         var v = this.getValue();
11452         if(String(v) !== String(this.startValue)){
11453             this.fireEvent('change', this, v, this.startValue);
11454         }
11455         
11456     },
11457     
11458     /**
11459      * Resets the current field value to the originally loaded value and clears any validation messages
11460      */
11461     reset : function(){
11462         this.setValue(this.originalValue);
11463         this.validate();
11464     },
11465      /**
11466      * Returns the name of the field
11467      * @return {Mixed} name The name field
11468      */
11469     getName: function(){
11470         return this.name;
11471     },
11472      /**
11473      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11474      * @return {Mixed} value The field value
11475      */
11476     getValue : function(){
11477         
11478         var v = this.inputEl().getValue();
11479         
11480         return v;
11481     },
11482     /**
11483      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11484      * @return {Mixed} value The field value
11485      */
11486     getRawValue : function(){
11487         var v = this.inputEl().getValue();
11488         
11489         return v;
11490     },
11491     
11492     /**
11493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11494      * @param {Mixed} value The value to set
11495      */
11496     setRawValue : function(v){
11497         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11498     },
11499     
11500     selectText : function(start, end){
11501         var v = this.getRawValue();
11502         if(v.length > 0){
11503             start = start === undefined ? 0 : start;
11504             end = end === undefined ? v.length : end;
11505             var d = this.inputEl().dom;
11506             if(d.setSelectionRange){
11507                 d.setSelectionRange(start, end);
11508             }else if(d.createTextRange){
11509                 var range = d.createTextRange();
11510                 range.moveStart("character", start);
11511                 range.moveEnd("character", v.length-end);
11512                 range.select();
11513             }
11514         }
11515     },
11516     
11517     /**
11518      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11519      * @param {Mixed} value The value to set
11520      */
11521     setValue : function(v){
11522         this.value = v;
11523         if(this.rendered){
11524             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11525             this.validate();
11526         }
11527     },
11528     
11529     /*
11530     processValue : function(value){
11531         if(this.stripCharsRe){
11532             var newValue = value.replace(this.stripCharsRe, '');
11533             if(newValue !== value){
11534                 this.setRawValue(newValue);
11535                 return newValue;
11536             }
11537         }
11538         return value;
11539     },
11540   */
11541     preFocus : function(){
11542         
11543         if(this.selectOnFocus){
11544             this.inputEl().dom.select();
11545         }
11546     },
11547     filterKeys : function(e){
11548         var k = e.getKey();
11549         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11550             return;
11551         }
11552         var c = e.getCharCode(), cc = String.fromCharCode(c);
11553         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11554             return;
11555         }
11556         if(!this.maskRe.test(cc)){
11557             e.stopEvent();
11558         }
11559     },
11560      /**
11561      * Clear any invalid styles/messages for this field
11562      */
11563     clearInvalid : function(){
11564         
11565         if(!this.el || this.preventMark){ // not rendered
11566             return;
11567         }
11568         
11569         
11570         this.el.removeClass([this.invalidClass, 'is-invalid']);
11571         
11572         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11573             
11574             var feedback = this.el.select('.form-control-feedback', true).first();
11575             
11576             if(feedback){
11577                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11578             }
11579             
11580         }
11581         
11582         if(this.indicator){
11583             this.indicator.removeClass('visible');
11584             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11585         }
11586         
11587         this.fireEvent('valid', this);
11588     },
11589     
11590      /**
11591      * Mark this field as valid
11592      */
11593     markValid : function()
11594     {
11595         if(!this.el  || this.preventMark){ // not rendered...
11596             return;
11597         }
11598         
11599         this.el.removeClass([this.invalidClass, this.validClass]);
11600         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11601
11602         var feedback = this.el.select('.form-control-feedback', true).first();
11603             
11604         if(feedback){
11605             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11606         }
11607         
11608         if(this.indicator){
11609             this.indicator.removeClass('visible');
11610             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11611         }
11612         
11613         if(this.disabled){
11614             return;
11615         }
11616         
11617            
11618         if(this.allowBlank && !this.getRawValue().length){
11619             return;
11620         }
11621         if (Roo.bootstrap.version == 3) {
11622             this.el.addClass(this.validClass);
11623         } else {
11624             this.inputEl().addClass('is-valid');
11625         }
11626
11627         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11628             
11629             var feedback = this.el.select('.form-control-feedback', true).first();
11630             
11631             if(feedback){
11632                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11634             }
11635             
11636         }
11637         
11638         this.fireEvent('valid', this);
11639     },
11640     
11641      /**
11642      * Mark this field as invalid
11643      * @param {String} msg The validation message
11644      */
11645     markInvalid : function(msg)
11646     {
11647         if(!this.el  || this.preventMark){ // not rendered
11648             return;
11649         }
11650         
11651         this.el.removeClass([this.invalidClass, this.validClass]);
11652         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11653         
11654         var feedback = this.el.select('.form-control-feedback', true).first();
11655             
11656         if(feedback){
11657             this.el.select('.form-control-feedback', true).first().removeClass(
11658                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11659         }
11660
11661         if(this.disabled){
11662             return;
11663         }
11664         
11665         if(this.allowBlank && !this.getRawValue().length){
11666             return;
11667         }
11668         
11669         if(this.indicator){
11670             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11671             this.indicator.addClass('visible');
11672         }
11673         if (Roo.bootstrap.version == 3) {
11674             this.el.addClass(this.invalidClass);
11675         } else {
11676             this.inputEl().addClass('is-invalid');
11677         }
11678         
11679         
11680         
11681         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11682             
11683             var feedback = this.el.select('.form-control-feedback', true).first();
11684             
11685             if(feedback){
11686                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11687                 
11688                 if(this.getValue().length || this.forceFeedback){
11689                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11690                 }
11691                 
11692             }
11693             
11694         }
11695         
11696         this.fireEvent('invalid', this, msg);
11697     },
11698     // private
11699     SafariOnKeyDown : function(event)
11700     {
11701         // this is a workaround for a password hang bug on chrome/ webkit.
11702         if (this.inputEl().dom.type != 'password') {
11703             return;
11704         }
11705         
11706         var isSelectAll = false;
11707         
11708         if(this.inputEl().dom.selectionEnd > 0){
11709             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11710         }
11711         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11712             event.preventDefault();
11713             this.setValue('');
11714             return;
11715         }
11716         
11717         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11718             
11719             event.preventDefault();
11720             // this is very hacky as keydown always get's upper case.
11721             //
11722             var cc = String.fromCharCode(event.getCharCode());
11723             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11724             
11725         }
11726     },
11727     adjustWidth : function(tag, w){
11728         tag = tag.toLowerCase();
11729         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11730             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11731                 if(tag == 'input'){
11732                     return w + 2;
11733                 }
11734                 if(tag == 'textarea'){
11735                     return w-2;
11736                 }
11737             }else if(Roo.isOpera){
11738                 if(tag == 'input'){
11739                     return w + 2;
11740                 }
11741                 if(tag == 'textarea'){
11742                     return w-2;
11743                 }
11744             }
11745         }
11746         return w;
11747     },
11748     
11749     setFieldLabel : function(v)
11750     {
11751         if(!this.rendered){
11752             return;
11753         }
11754         
11755         if(this.indicatorEl()){
11756             var ar = this.el.select('label > span',true);
11757             
11758             if (ar.elements.length) {
11759                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11760                 this.fieldLabel = v;
11761                 return;
11762             }
11763             
11764             var br = this.el.select('label',true);
11765             
11766             if(br.elements.length) {
11767                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11768                 this.fieldLabel = v;
11769                 return;
11770             }
11771             
11772             Roo.log('Cannot Found any of label > span || label in input');
11773             return;
11774         }
11775         
11776         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11777         this.fieldLabel = v;
11778         
11779         
11780     }
11781 });
11782
11783  
11784 /*
11785  * - LGPL
11786  *
11787  * Input
11788  * 
11789  */
11790
11791 /**
11792  * @class Roo.bootstrap.TextArea
11793  * @extends Roo.bootstrap.Input
11794  * Bootstrap TextArea class
11795  * @cfg {Number} cols Specifies the visible width of a text area
11796  * @cfg {Number} rows Specifies the visible number of lines in a text area
11797  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11798  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11799  * @cfg {string} html text
11800  * 
11801  * @constructor
11802  * Create a new TextArea
11803  * @param {Object} config The config object
11804  */
11805
11806 Roo.bootstrap.TextArea = function(config){
11807     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11808    
11809 };
11810
11811 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11812      
11813     cols : false,
11814     rows : 5,
11815     readOnly : false,
11816     warp : 'soft',
11817     resize : false,
11818     value: false,
11819     html: false,
11820     
11821     getAutoCreate : function(){
11822         
11823         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11824         
11825         var id = Roo.id();
11826         
11827         var cfg = {};
11828         
11829         if(this.inputType != 'hidden'){
11830             cfg.cls = 'form-group' //input-group
11831         }
11832         
11833         var input =  {
11834             tag: 'textarea',
11835             id : id,
11836             warp : this.warp,
11837             rows : this.rows,
11838             value : this.value || '',
11839             html: this.html || '',
11840             cls : 'form-control',
11841             placeholder : this.placeholder || '' 
11842             
11843         };
11844         
11845         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11846             input.maxLength = this.maxLength;
11847         }
11848         
11849         if(this.resize){
11850             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11851         }
11852         
11853         if(this.cols){
11854             input.cols = this.cols;
11855         }
11856         
11857         if (this.readOnly) {
11858             input.readonly = true;
11859         }
11860         
11861         if (this.name) {
11862             input.name = this.name;
11863         }
11864         
11865         if (this.size) {
11866             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11867         }
11868         
11869         var settings=this;
11870         ['xs','sm','md','lg'].map(function(size){
11871             if (settings[size]) {
11872                 cfg.cls += ' col-' + size + '-' + settings[size];
11873             }
11874         });
11875         
11876         var inputblock = input;
11877         
11878         if(this.hasFeedback && !this.allowBlank){
11879             
11880             var feedback = {
11881                 tag: 'span',
11882                 cls: 'glyphicon form-control-feedback'
11883             };
11884
11885             inputblock = {
11886                 cls : 'has-feedback',
11887                 cn :  [
11888                     input,
11889                     feedback
11890                 ] 
11891             };  
11892         }
11893         
11894         
11895         if (this.before || this.after) {
11896             
11897             inputblock = {
11898                 cls : 'input-group',
11899                 cn :  [] 
11900             };
11901             if (this.before) {
11902                 inputblock.cn.push({
11903                     tag :'span',
11904                     cls : 'input-group-addon',
11905                     html : this.before
11906                 });
11907             }
11908             
11909             inputblock.cn.push(input);
11910             
11911             if(this.hasFeedback && !this.allowBlank){
11912                 inputblock.cls += ' has-feedback';
11913                 inputblock.cn.push(feedback);
11914             }
11915             
11916             if (this.after) {
11917                 inputblock.cn.push({
11918                     tag :'span',
11919                     cls : 'input-group-addon',
11920                     html : this.after
11921                 });
11922             }
11923             
11924         }
11925         
11926         if (align ==='left' && this.fieldLabel.length) {
11927             cfg.cn = [
11928                 {
11929                     tag: 'label',
11930                     'for' :  id,
11931                     cls : 'control-label',
11932                     html : this.fieldLabel
11933                 },
11934                 {
11935                     cls : "",
11936                     cn: [
11937                         inputblock
11938                     ]
11939                 }
11940
11941             ];
11942             
11943             if(this.labelWidth > 12){
11944                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11945             }
11946
11947             if(this.labelWidth < 13 && this.labelmd == 0){
11948                 this.labelmd = this.labelWidth;
11949             }
11950
11951             if(this.labellg > 0){
11952                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11953                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11954             }
11955
11956             if(this.labelmd > 0){
11957                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11958                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11959             }
11960
11961             if(this.labelsm > 0){
11962                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11963                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11964             }
11965
11966             if(this.labelxs > 0){
11967                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11968                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11969             }
11970             
11971         } else if ( this.fieldLabel.length) {
11972             cfg.cn = [
11973
11974                {
11975                    tag: 'label',
11976                    //cls : 'input-group-addon',
11977                    html : this.fieldLabel
11978
11979                },
11980
11981                inputblock
11982
11983            ];
11984
11985         } else {
11986
11987             cfg.cn = [
11988
11989                 inputblock
11990
11991             ];
11992                 
11993         }
11994         
11995         if (this.disabled) {
11996             input.disabled=true;
11997         }
11998         
11999         return cfg;
12000         
12001     },
12002     /**
12003      * return the real textarea element.
12004      */
12005     inputEl: function ()
12006     {
12007         return this.el.select('textarea.form-control',true).first();
12008     },
12009     
12010     /**
12011      * Clear any invalid styles/messages for this field
12012      */
12013     clearInvalid : function()
12014     {
12015         
12016         if(!this.el || this.preventMark){ // not rendered
12017             return;
12018         }
12019         
12020         var label = this.el.select('label', true).first();
12021         var icon = this.el.select('i.fa-star', true).first();
12022         
12023         if(label && icon){
12024             icon.remove();
12025         }
12026         this.el.removeClass( this.validClass);
12027         this.inputEl().removeClass('is-invalid');
12028          
12029         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12030             
12031             var feedback = this.el.select('.form-control-feedback', true).first();
12032             
12033             if(feedback){
12034                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12035             }
12036             
12037         }
12038         
12039         this.fireEvent('valid', this);
12040     },
12041     
12042      /**
12043      * Mark this field as valid
12044      */
12045     markValid : function()
12046     {
12047         if(!this.el  || this.preventMark){ // not rendered
12048             return;
12049         }
12050         
12051         this.el.removeClass([this.invalidClass, this.validClass]);
12052         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12053         
12054         var feedback = this.el.select('.form-control-feedback', true).first();
12055             
12056         if(feedback){
12057             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12058         }
12059
12060         if(this.disabled || this.allowBlank){
12061             return;
12062         }
12063         
12064         var label = this.el.select('label', true).first();
12065         var icon = this.el.select('i.fa-star', true).first();
12066         
12067         if(label && icon){
12068             icon.remove();
12069         }
12070         if (Roo.bootstrap.version == 3) {
12071             this.el.addClass(this.validClass);
12072         } else {
12073             this.inputEl().addClass('is-valid');
12074         }
12075         
12076         
12077         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12078             
12079             var feedback = this.el.select('.form-control-feedback', true).first();
12080             
12081             if(feedback){
12082                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12083                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12084             }
12085             
12086         }
12087         
12088         this.fireEvent('valid', this);
12089     },
12090     
12091      /**
12092      * Mark this field as invalid
12093      * @param {String} msg The validation message
12094      */
12095     markInvalid : function(msg)
12096     {
12097         if(!this.el  || this.preventMark){ // not rendered
12098             return;
12099         }
12100         
12101         this.el.removeClass([this.invalidClass, this.validClass]);
12102         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12103         
12104         var feedback = this.el.select('.form-control-feedback', true).first();
12105             
12106         if(feedback){
12107             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12108         }
12109
12110         if(this.disabled || this.allowBlank){
12111             return;
12112         }
12113         
12114         var label = this.el.select('label', true).first();
12115         var icon = this.el.select('i.fa-star', true).first();
12116         
12117         if(!this.getValue().length && label && !icon){
12118             this.el.createChild({
12119                 tag : 'i',
12120                 cls : 'text-danger fa fa-lg fa-star',
12121                 tooltip : 'This field is required',
12122                 style : 'margin-right:5px;'
12123             }, label, true);
12124         }
12125         
12126         if (Roo.bootstrap.version == 3) {
12127             this.el.addClass(this.invalidClass);
12128         } else {
12129             this.inputEl().addClass('is-invalid');
12130         }
12131         
12132         // fixme ... this may be depricated need to test..
12133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12134             
12135             var feedback = this.el.select('.form-control-feedback', true).first();
12136             
12137             if(feedback){
12138                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12139                 
12140                 if(this.getValue().length || this.forceFeedback){
12141                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12142                 }
12143                 
12144             }
12145             
12146         }
12147         
12148         this.fireEvent('invalid', this, msg);
12149     }
12150 });
12151
12152  
12153 /*
12154  * - LGPL
12155  *
12156  * trigger field - base class for combo..
12157  * 
12158  */
12159  
12160 /**
12161  * @class Roo.bootstrap.TriggerField
12162  * @extends Roo.bootstrap.Input
12163  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12164  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12165  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12166  * for which you can provide a custom implementation.  For example:
12167  * <pre><code>
12168 var trigger = new Roo.bootstrap.TriggerField();
12169 trigger.onTriggerClick = myTriggerFn;
12170 trigger.applyTo('my-field');
12171 </code></pre>
12172  *
12173  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12174  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12175  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12176  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12177  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12178
12179  * @constructor
12180  * Create a new TriggerField.
12181  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12182  * to the base TextField)
12183  */
12184 Roo.bootstrap.TriggerField = function(config){
12185     this.mimicing = false;
12186     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12187 };
12188
12189 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12190     /**
12191      * @cfg {String} triggerClass A CSS class to apply to the trigger
12192      */
12193      /**
12194      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12195      */
12196     hideTrigger:false,
12197
12198     /**
12199      * @cfg {Boolean} removable (true|false) special filter default false
12200      */
12201     removable : false,
12202     
12203     /** @cfg {Boolean} grow @hide */
12204     /** @cfg {Number} growMin @hide */
12205     /** @cfg {Number} growMax @hide */
12206
12207     /**
12208      * @hide 
12209      * @method
12210      */
12211     autoSize: Roo.emptyFn,
12212     // private
12213     monitorTab : true,
12214     // private
12215     deferHeight : true,
12216
12217     
12218     actionMode : 'wrap',
12219     
12220     caret : false,
12221     
12222     
12223     getAutoCreate : function(){
12224        
12225         var align = this.labelAlign || this.parentLabelAlign();
12226         
12227         var id = Roo.id();
12228         
12229         var cfg = {
12230             cls: 'form-group' //input-group
12231         };
12232         
12233         
12234         var input =  {
12235             tag: 'input',
12236             id : id,
12237             type : this.inputType,
12238             cls : 'form-control',
12239             autocomplete: 'new-password',
12240             placeholder : this.placeholder || '' 
12241             
12242         };
12243         if (this.name) {
12244             input.name = this.name;
12245         }
12246         if (this.size) {
12247             input.cls += ' input-' + this.size;
12248         }
12249         
12250         if (this.disabled) {
12251             input.disabled=true;
12252         }
12253         
12254         var inputblock = input;
12255         
12256         if(this.hasFeedback && !this.allowBlank){
12257             
12258             var feedback = {
12259                 tag: 'span',
12260                 cls: 'glyphicon form-control-feedback'
12261             };
12262             
12263             if(this.removable && !this.editable  ){
12264                 inputblock = {
12265                     cls : 'has-feedback',
12266                     cn :  [
12267                         inputblock,
12268                         {
12269                             tag: 'button',
12270                             html : 'x',
12271                             cls : 'roo-combo-removable-btn close'
12272                         },
12273                         feedback
12274                     ] 
12275                 };
12276             } else {
12277                 inputblock = {
12278                     cls : 'has-feedback',
12279                     cn :  [
12280                         inputblock,
12281                         feedback
12282                     ] 
12283                 };
12284             }
12285
12286         } else {
12287             if(this.removable && !this.editable ){
12288                 inputblock = {
12289                     cls : 'roo-removable',
12290                     cn :  [
12291                         inputblock,
12292                         {
12293                             tag: 'button',
12294                             html : 'x',
12295                             cls : 'roo-combo-removable-btn close'
12296                         }
12297                     ] 
12298                 };
12299             }
12300         }
12301         
12302         if (this.before || this.after) {
12303             
12304             inputblock = {
12305                 cls : 'input-group',
12306                 cn :  [] 
12307             };
12308             if (this.before) {
12309                 inputblock.cn.push({
12310                     tag :'span',
12311                     cls : 'input-group-addon input-group-prepend input-group-text',
12312                     html : this.before
12313                 });
12314             }
12315             
12316             inputblock.cn.push(input);
12317             
12318             if(this.hasFeedback && !this.allowBlank){
12319                 inputblock.cls += ' has-feedback';
12320                 inputblock.cn.push(feedback);
12321             }
12322             
12323             if (this.after) {
12324                 inputblock.cn.push({
12325                     tag :'span',
12326                     cls : 'input-group-addon input-group-append input-group-text',
12327                     html : this.after
12328                 });
12329             }
12330             
12331         };
12332         
12333       
12334         
12335         var ibwrap = inputblock;
12336         
12337         if(this.multiple){
12338             ibwrap = {
12339                 tag: 'ul',
12340                 cls: 'roo-select2-choices',
12341                 cn:[
12342                     {
12343                         tag: 'li',
12344                         cls: 'roo-select2-search-field',
12345                         cn: [
12346
12347                             inputblock
12348                         ]
12349                     }
12350                 ]
12351             };
12352                 
12353         }
12354         
12355         var combobox = {
12356             cls: 'roo-select2-container input-group',
12357             cn: [
12358                  {
12359                     tag: 'input',
12360                     type : 'hidden',
12361                     cls: 'form-hidden-field'
12362                 },
12363                 ibwrap
12364             ]
12365         };
12366         
12367         if(!this.multiple && this.showToggleBtn){
12368             
12369             var caret = {
12370                         tag: 'span',
12371                         cls: 'caret'
12372              };
12373             if (this.caret != false) {
12374                 caret = {
12375                      tag: 'i',
12376                      cls: 'fa fa-' + this.caret
12377                 };
12378                 
12379             }
12380             
12381             combobox.cn.push({
12382                 tag :'span',
12383                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12384                 cn : [
12385                     Roo.bootstrap.version == 3 ? caret : '',
12386                     {
12387                         tag: 'span',
12388                         cls: 'combobox-clear',
12389                         cn  : [
12390                             {
12391                                 tag : 'i',
12392                                 cls: 'icon-remove'
12393                             }
12394                         ]
12395                     }
12396                 ]
12397
12398             })
12399         }
12400         
12401         if(this.multiple){
12402             combobox.cls += ' roo-select2-container-multi';
12403         }
12404          var indicator = {
12405             tag : 'i',
12406             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12407             tooltip : 'This field is required'
12408         };
12409         if (Roo.bootstrap.version == 4) {
12410             indicator = {
12411                 tag : 'i',
12412                 style : 'display:none'
12413             };
12414         }
12415         
12416         
12417         if (align ==='left' && this.fieldLabel.length) {
12418             
12419             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12420
12421             cfg.cn = [
12422                 indicator,
12423                 {
12424                     tag: 'label',
12425                     'for' :  id,
12426                     cls : 'control-label',
12427                     html : this.fieldLabel
12428
12429                 },
12430                 {
12431                     cls : "", 
12432                     cn: [
12433                         combobox
12434                     ]
12435                 }
12436
12437             ];
12438             
12439             var labelCfg = cfg.cn[1];
12440             var contentCfg = cfg.cn[2];
12441             
12442             if(this.indicatorpos == 'right'){
12443                 cfg.cn = [
12444                     {
12445                         tag: 'label',
12446                         'for' :  id,
12447                         cls : 'control-label',
12448                         cn : [
12449                             {
12450                                 tag : 'span',
12451                                 html : this.fieldLabel
12452                             },
12453                             indicator
12454                         ]
12455                     },
12456                     {
12457                         cls : "", 
12458                         cn: [
12459                             combobox
12460                         ]
12461                     }
12462
12463                 ];
12464                 
12465                 labelCfg = cfg.cn[0];
12466                 contentCfg = cfg.cn[1];
12467             }
12468             
12469             if(this.labelWidth > 12){
12470                 labelCfg.style = "width: " + this.labelWidth + 'px';
12471             }
12472             
12473             if(this.labelWidth < 13 && this.labelmd == 0){
12474                 this.labelmd = this.labelWidth;
12475             }
12476             
12477             if(this.labellg > 0){
12478                 labelCfg.cls += ' col-lg-' + this.labellg;
12479                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12480             }
12481             
12482             if(this.labelmd > 0){
12483                 labelCfg.cls += ' col-md-' + this.labelmd;
12484                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12485             }
12486             
12487             if(this.labelsm > 0){
12488                 labelCfg.cls += ' col-sm-' + this.labelsm;
12489                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12490             }
12491             
12492             if(this.labelxs > 0){
12493                 labelCfg.cls += ' col-xs-' + this.labelxs;
12494                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12495             }
12496             
12497         } else if ( this.fieldLabel.length) {
12498 //                Roo.log(" label");
12499             cfg.cn = [
12500                 indicator,
12501                {
12502                    tag: 'label',
12503                    //cls : 'input-group-addon',
12504                    html : this.fieldLabel
12505
12506                },
12507
12508                combobox
12509
12510             ];
12511             
12512             if(this.indicatorpos == 'right'){
12513                 
12514                 cfg.cn = [
12515                     {
12516                        tag: 'label',
12517                        cn : [
12518                            {
12519                                tag : 'span',
12520                                html : this.fieldLabel
12521                            },
12522                            indicator
12523                        ]
12524
12525                     },
12526                     combobox
12527
12528                 ];
12529
12530             }
12531
12532         } else {
12533             
12534 //                Roo.log(" no label && no align");
12535                 cfg = combobox
12536                      
12537                 
12538         }
12539         
12540         var settings=this;
12541         ['xs','sm','md','lg'].map(function(size){
12542             if (settings[size]) {
12543                 cfg.cls += ' col-' + size + '-' + settings[size];
12544             }
12545         });
12546         
12547         return cfg;
12548         
12549     },
12550     
12551     
12552     
12553     // private
12554     onResize : function(w, h){
12555 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12556 //        if(typeof w == 'number'){
12557 //            var x = w - this.trigger.getWidth();
12558 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12559 //            this.trigger.setStyle('left', x+'px');
12560 //        }
12561     },
12562
12563     // private
12564     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12565
12566     // private
12567     getResizeEl : function(){
12568         return this.inputEl();
12569     },
12570
12571     // private
12572     getPositionEl : function(){
12573         return this.inputEl();
12574     },
12575
12576     // private
12577     alignErrorIcon : function(){
12578         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12579     },
12580
12581     // private
12582     initEvents : function(){
12583         
12584         this.createList();
12585         
12586         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12587         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12588         if(!this.multiple && this.showToggleBtn){
12589             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12590             if(this.hideTrigger){
12591                 this.trigger.setDisplayed(false);
12592             }
12593             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12594         }
12595         
12596         if(this.multiple){
12597             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12598         }
12599         
12600         if(this.removable && !this.editable && !this.tickable){
12601             var close = this.closeTriggerEl();
12602             
12603             if(close){
12604                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12605                 close.on('click', this.removeBtnClick, this, close);
12606             }
12607         }
12608         
12609         //this.trigger.addClassOnOver('x-form-trigger-over');
12610         //this.trigger.addClassOnClick('x-form-trigger-click');
12611         
12612         //if(!this.width){
12613         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12614         //}
12615     },
12616     
12617     closeTriggerEl : function()
12618     {
12619         var close = this.el.select('.roo-combo-removable-btn', true).first();
12620         return close ? close : false;
12621     },
12622     
12623     removeBtnClick : function(e, h, el)
12624     {
12625         e.preventDefault();
12626         
12627         if(this.fireEvent("remove", this) !== false){
12628             this.reset();
12629             this.fireEvent("afterremove", this)
12630         }
12631     },
12632     
12633     createList : function()
12634     {
12635         this.list = Roo.get(document.body).createChild({
12636             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12637             cls: 'typeahead typeahead-long dropdown-menu shadow',
12638             style: 'display:none'
12639         });
12640         
12641         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12642         
12643     },
12644
12645     // private
12646     initTrigger : function(){
12647        
12648     },
12649
12650     // private
12651     onDestroy : function(){
12652         if(this.trigger){
12653             this.trigger.removeAllListeners();
12654           //  this.trigger.remove();
12655         }
12656         //if(this.wrap){
12657         //    this.wrap.remove();
12658         //}
12659         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12660     },
12661
12662     // private
12663     onFocus : function(){
12664         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12665         /*
12666         if(!this.mimicing){
12667             this.wrap.addClass('x-trigger-wrap-focus');
12668             this.mimicing = true;
12669             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12670             if(this.monitorTab){
12671                 this.el.on("keydown", this.checkTab, this);
12672             }
12673         }
12674         */
12675     },
12676
12677     // private
12678     checkTab : function(e){
12679         if(e.getKey() == e.TAB){
12680             this.triggerBlur();
12681         }
12682     },
12683
12684     // private
12685     onBlur : function(){
12686         // do nothing
12687     },
12688
12689     // private
12690     mimicBlur : function(e, t){
12691         /*
12692         if(!this.wrap.contains(t) && this.validateBlur()){
12693             this.triggerBlur();
12694         }
12695         */
12696     },
12697
12698     // private
12699     triggerBlur : function(){
12700         this.mimicing = false;
12701         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12702         if(this.monitorTab){
12703             this.el.un("keydown", this.checkTab, this);
12704         }
12705         //this.wrap.removeClass('x-trigger-wrap-focus');
12706         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12707     },
12708
12709     // private
12710     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12711     validateBlur : function(e, t){
12712         return true;
12713     },
12714
12715     // private
12716     onDisable : function(){
12717         this.inputEl().dom.disabled = true;
12718         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12719         //if(this.wrap){
12720         //    this.wrap.addClass('x-item-disabled');
12721         //}
12722     },
12723
12724     // private
12725     onEnable : function(){
12726         this.inputEl().dom.disabled = false;
12727         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12728         //if(this.wrap){
12729         //    this.el.removeClass('x-item-disabled');
12730         //}
12731     },
12732
12733     // private
12734     onShow : function(){
12735         var ae = this.getActionEl();
12736         
12737         if(ae){
12738             ae.dom.style.display = '';
12739             ae.dom.style.visibility = 'visible';
12740         }
12741     },
12742
12743     // private
12744     
12745     onHide : function(){
12746         var ae = this.getActionEl();
12747         ae.dom.style.display = 'none';
12748     },
12749
12750     /**
12751      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12752      * by an implementing function.
12753      * @method
12754      * @param {EventObject} e
12755      */
12756     onTriggerClick : Roo.emptyFn
12757 });
12758  
12759 /*
12760 * Licence: LGPL
12761 */
12762
12763 /**
12764  * @class Roo.bootstrap.CardUploader
12765  * @extends Roo.bootstrap.Button
12766  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12767  * @cfg {Number} errorTimeout default 3000
12768  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12769  * @cfg {Array}  html The button text.
12770
12771  *
12772  * @constructor
12773  * Create a new CardUploader
12774  * @param {Object} config The config object
12775  */
12776
12777 Roo.bootstrap.CardUploader = function(config){
12778     
12779  
12780     
12781     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12782     
12783     
12784     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12785         return r.data.id
12786      });
12787     
12788      this.addEvents({
12789          // raw events
12790         /**
12791          * @event preview
12792          * When a image is clicked on - and needs to display a slideshow or similar..
12793          * @param {Roo.bootstrap.Card} this
12794          * @param {Object} The image information data 
12795          *
12796          */
12797         'preview' : true,
12798          /**
12799          * @event download
12800          * When a the download link is clicked
12801          * @param {Roo.bootstrap.Card} this
12802          * @param {Object} The image information data  contains 
12803          */
12804         'download' : true
12805         
12806     });
12807 };
12808  
12809 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12810     
12811      
12812     errorTimeout : 3000,
12813      
12814     images : false,
12815    
12816     fileCollection : false,
12817     allowBlank : true,
12818     
12819     getAutoCreate : function()
12820     {
12821         
12822         var cfg =  {
12823             cls :'form-group' ,
12824             cn : [
12825                
12826                 {
12827                     tag: 'label',
12828                    //cls : 'input-group-addon',
12829                     html : this.fieldLabel
12830
12831                 },
12832
12833                 {
12834                     tag: 'input',
12835                     type : 'hidden',
12836                     name : this.name,
12837                     value : this.value,
12838                     cls : 'd-none  form-control'
12839                 },
12840                 
12841                 {
12842                     tag: 'input',
12843                     multiple : 'multiple',
12844                     type : 'file',
12845                     cls : 'd-none  roo-card-upload-selector'
12846                 },
12847                 
12848                 {
12849                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12850                 },
12851                 {
12852                     cls : 'card-columns roo-card-uploader-container'
12853                 }
12854
12855             ]
12856         };
12857            
12858          
12859         return cfg;
12860     },
12861     
12862     getChildContainer : function() /// what children are added to.
12863     {
12864         return this.containerEl;
12865     },
12866    
12867     getButtonContainer : function() /// what children are added to.
12868     {
12869         return this.el.select(".roo-card-uploader-button-container").first();
12870     },
12871    
12872     initEvents : function()
12873     {
12874         
12875         Roo.bootstrap.Input.prototype.initEvents.call(this);
12876         
12877         var t = this;
12878         this.addxtype({
12879             xns: Roo.bootstrap,
12880
12881             xtype : 'Button',
12882             container_method : 'getButtonContainer' ,            
12883             html :  this.html, // fix changable?
12884             cls : 'w-100 ',
12885             listeners : {
12886                 'click' : function(btn, e) {
12887                     t.onClick(e);
12888                 }
12889             }
12890         });
12891         
12892         
12893         
12894         
12895         this.urlAPI = (window.createObjectURL && window) || 
12896                                 (window.URL && URL.revokeObjectURL && URL) || 
12897                                 (window.webkitURL && webkitURL);
12898                         
12899          
12900          
12901          
12902         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12903         
12904         this.selectorEl.on('change', this.onFileSelected, this);
12905         if (this.images) {
12906             var t = this;
12907             this.images.forEach(function(img) {
12908                 t.addCard(img)
12909             });
12910             this.images = false;
12911         }
12912         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12913          
12914        
12915     },
12916     
12917    
12918     onClick : function(e)
12919     {
12920         e.preventDefault();
12921          
12922         this.selectorEl.dom.click();
12923          
12924     },
12925     
12926     onFileSelected : function(e)
12927     {
12928         e.preventDefault();
12929         
12930         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12931             return;
12932         }
12933         
12934         Roo.each(this.selectorEl.dom.files, function(file){    
12935             this.addFile(file);
12936         }, this);
12937          
12938     },
12939     
12940       
12941     
12942       
12943     
12944     addFile : function(file)
12945     {
12946            
12947         if(typeof(file) === 'string'){
12948             throw "Add file by name?"; // should not happen
12949             return;
12950         }
12951         
12952         if(!file || !this.urlAPI){
12953             return;
12954         }
12955         
12956         // file;
12957         // file.type;
12958         
12959         var _this = this;
12960         
12961         
12962         var url = _this.urlAPI.createObjectURL( file);
12963            
12964         this.addCard({
12965             id : Roo.bootstrap.CardUploader.ID--,
12966             is_uploaded : false,
12967             src : url,
12968             srcfile : file,
12969             title : file.name,
12970             mimetype : file.type,
12971             preview : false,
12972             is_deleted : 0
12973         });
12974         
12975     },
12976     
12977     /**
12978      * addCard - add an Attachment to the uploader
12979      * @param data - the data about the image to upload
12980      *
12981      * {
12982           id : 123
12983           title : "Title of file",
12984           is_uploaded : false,
12985           src : "http://.....",
12986           srcfile : { the File upload object },
12987           mimetype : file.type,
12988           preview : false,
12989           is_deleted : 0
12990           .. any other data...
12991         }
12992      *
12993      * 
12994     */
12995     
12996     addCard : function (data)
12997     {
12998         // hidden input element?
12999         // if the file is not an image...
13000         //then we need to use something other that and header_image
13001         var t = this;
13002         //   remove.....
13003         var footer = [
13004             {
13005                 xns : Roo.bootstrap,
13006                 xtype : 'CardFooter',
13007                  items: [
13008                     {
13009                         xns : Roo.bootstrap,
13010                         xtype : 'Element',
13011                         cls : 'd-flex',
13012                         items : [
13013                             
13014                             {
13015                                 xns : Roo.bootstrap,
13016                                 xtype : 'Button',
13017                                 html : String.format("<small>{0}</small>", data.title),
13018                                 cls : 'col-10 text-left',
13019                                 size: 'sm',
13020                                 weight: 'link',
13021                                 fa : 'download',
13022                                 listeners : {
13023                                     click : function() {
13024                                      
13025                                         t.fireEvent( "download", t, data );
13026                                     }
13027                                 }
13028                             },
13029                           
13030                             {
13031                                 xns : Roo.bootstrap,
13032                                 xtype : 'Button',
13033                                 style: 'max-height: 28px; ',
13034                                 size : 'sm',
13035                                 weight: 'danger',
13036                                 cls : 'col-2',
13037                                 fa : 'times',
13038                                 listeners : {
13039                                     click : function() {
13040                                         t.removeCard(data.id)
13041                                     }
13042                                 }
13043                             }
13044                         ]
13045                     }
13046                     
13047                 ] 
13048             }
13049             
13050         ];
13051         
13052         var cn = this.addxtype(
13053             {
13054                  
13055                 xns : Roo.bootstrap,
13056                 xtype : 'Card',
13057                 closeable : true,
13058                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13059                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13060                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13061                 data : data,
13062                 html : false,
13063                  
13064                 items : footer,
13065                 initEvents : function() {
13066                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13067                     var card = this;
13068                     this.imgEl = this.el.select('.card-img-top').first();
13069                     if (this.imgEl) {
13070                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13071                         this.imgEl.set({ 'pointer' : 'cursor' });
13072                                   
13073                     }
13074                     this.getCardFooter().addClass('p-1');
13075                     
13076                   
13077                 }
13078                 
13079             }
13080         );
13081         // dont' really need ot update items.
13082         // this.items.push(cn);
13083         this.fileCollection.add(cn);
13084         
13085         if (!data.srcfile) {
13086             this.updateInput();
13087             return;
13088         }
13089             
13090         var _t = this;
13091         var reader = new FileReader();
13092         reader.addEventListener("load", function() {  
13093             data.srcdata =  reader.result;
13094             _t.updateInput();
13095         });
13096         reader.readAsDataURL(data.srcfile);
13097         
13098         
13099         
13100     },
13101     removeCard : function(id)
13102     {
13103         
13104         var card  = this.fileCollection.get(id);
13105         card.data.is_deleted = 1;
13106         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13107         //this.fileCollection.remove(card);
13108         //this.items = this.items.filter(function(e) { return e != card });
13109         // dont' really need ot update items.
13110         card.el.dom.parentNode.removeChild(card.el.dom);
13111         this.updateInput();
13112
13113         
13114     },
13115     reset: function()
13116     {
13117         this.fileCollection.each(function(card) {
13118             if (card.el.dom && card.el.dom.parentNode) {
13119                 card.el.dom.parentNode.removeChild(card.el.dom);
13120             }
13121         });
13122         this.fileCollection.clear();
13123         this.updateInput();
13124     },
13125     
13126     updateInput : function()
13127     {
13128          var data = [];
13129         this.fileCollection.each(function(e) {
13130             data.push(e.data);
13131             
13132         });
13133         this.inputEl().dom.value = JSON.stringify(data);
13134         
13135         
13136         
13137     }
13138     
13139     
13140 });
13141
13142
13143 Roo.bootstrap.CardUploader.ID = -1;/*
13144  * Based on:
13145  * Ext JS Library 1.1.1
13146  * Copyright(c) 2006-2007, Ext JS, LLC.
13147  *
13148  * Originally Released Under LGPL - original licence link has changed is not relivant.
13149  *
13150  * Fork - LGPL
13151  * <script type="text/javascript">
13152  */
13153
13154
13155 /**
13156  * @class Roo.data.SortTypes
13157  * @singleton
13158  * Defines the default sorting (casting?) comparison functions used when sorting data.
13159  */
13160 Roo.data.SortTypes = {
13161     /**
13162      * Default sort that does nothing
13163      * @param {Mixed} s The value being converted
13164      * @return {Mixed} The comparison value
13165      */
13166     none : function(s){
13167         return s;
13168     },
13169     
13170     /**
13171      * The regular expression used to strip tags
13172      * @type {RegExp}
13173      * @property
13174      */
13175     stripTagsRE : /<\/?[^>]+>/gi,
13176     
13177     /**
13178      * Strips all HTML tags to sort on text only
13179      * @param {Mixed} s The value being converted
13180      * @return {String} The comparison value
13181      */
13182     asText : function(s){
13183         return String(s).replace(this.stripTagsRE, "");
13184     },
13185     
13186     /**
13187      * Strips all HTML tags to sort on text only - Case insensitive
13188      * @param {Mixed} s The value being converted
13189      * @return {String} The comparison value
13190      */
13191     asUCText : function(s){
13192         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13193     },
13194     
13195     /**
13196      * Case insensitive string
13197      * @param {Mixed} s The value being converted
13198      * @return {String} The comparison value
13199      */
13200     asUCString : function(s) {
13201         return String(s).toUpperCase();
13202     },
13203     
13204     /**
13205      * Date sorting
13206      * @param {Mixed} s The value being converted
13207      * @return {Number} The comparison value
13208      */
13209     asDate : function(s) {
13210         if(!s){
13211             return 0;
13212         }
13213         if(s instanceof Date){
13214             return s.getTime();
13215         }
13216         return Date.parse(String(s));
13217     },
13218     
13219     /**
13220      * Float sorting
13221      * @param {Mixed} s The value being converted
13222      * @return {Float} The comparison value
13223      */
13224     asFloat : function(s) {
13225         var val = parseFloat(String(s).replace(/,/g, ""));
13226         if(isNaN(val)) {
13227             val = 0;
13228         }
13229         return val;
13230     },
13231     
13232     /**
13233      * Integer sorting
13234      * @param {Mixed} s The value being converted
13235      * @return {Number} The comparison value
13236      */
13237     asInt : function(s) {
13238         var val = parseInt(String(s).replace(/,/g, ""));
13239         if(isNaN(val)) {
13240             val = 0;
13241         }
13242         return val;
13243     }
13244 };/*
13245  * Based on:
13246  * Ext JS Library 1.1.1
13247  * Copyright(c) 2006-2007, Ext JS, LLC.
13248  *
13249  * Originally Released Under LGPL - original licence link has changed is not relivant.
13250  *
13251  * Fork - LGPL
13252  * <script type="text/javascript">
13253  */
13254
13255 /**
13256 * @class Roo.data.Record
13257  * Instances of this class encapsulate both record <em>definition</em> information, and record
13258  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13259  * to access Records cached in an {@link Roo.data.Store} object.<br>
13260  * <p>
13261  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13262  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13263  * objects.<br>
13264  * <p>
13265  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13266  * @constructor
13267  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13268  * {@link #create}. The parameters are the same.
13269  * @param {Array} data An associative Array of data values keyed by the field name.
13270  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13271  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13272  * not specified an integer id is generated.
13273  */
13274 Roo.data.Record = function(data, id){
13275     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13276     this.data = data;
13277 };
13278
13279 /**
13280  * Generate a constructor for a specific record layout.
13281  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13282  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13283  * Each field definition object may contain the following properties: <ul>
13284  * <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,
13285  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13286  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13287  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13288  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13289  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13290  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13291  * this may be omitted.</p></li>
13292  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13293  * <ul><li>auto (Default, implies no conversion)</li>
13294  * <li>string</li>
13295  * <li>int</li>
13296  * <li>float</li>
13297  * <li>boolean</li>
13298  * <li>date</li></ul></p></li>
13299  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13300  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13301  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13302  * by the Reader into an object that will be stored in the Record. It is passed the
13303  * following parameters:<ul>
13304  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13305  * </ul></p></li>
13306  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13307  * </ul>
13308  * <br>usage:<br><pre><code>
13309 var TopicRecord = Roo.data.Record.create(
13310     {name: 'title', mapping: 'topic_title'},
13311     {name: 'author', mapping: 'username'},
13312     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13313     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13314     {name: 'lastPoster', mapping: 'user2'},
13315     {name: 'excerpt', mapping: 'post_text'}
13316 );
13317
13318 var myNewRecord = new TopicRecord({
13319     title: 'Do my job please',
13320     author: 'noobie',
13321     totalPosts: 1,
13322     lastPost: new Date(),
13323     lastPoster: 'Animal',
13324     excerpt: 'No way dude!'
13325 });
13326 myStore.add(myNewRecord);
13327 </code></pre>
13328  * @method create
13329  * @static
13330  */
13331 Roo.data.Record.create = function(o){
13332     var f = function(){
13333         f.superclass.constructor.apply(this, arguments);
13334     };
13335     Roo.extend(f, Roo.data.Record);
13336     var p = f.prototype;
13337     p.fields = new Roo.util.MixedCollection(false, function(field){
13338         return field.name;
13339     });
13340     for(var i = 0, len = o.length; i < len; i++){
13341         p.fields.add(new Roo.data.Field(o[i]));
13342     }
13343     f.getField = function(name){
13344         return p.fields.get(name);  
13345     };
13346     return f;
13347 };
13348
13349 Roo.data.Record.AUTO_ID = 1000;
13350 Roo.data.Record.EDIT = 'edit';
13351 Roo.data.Record.REJECT = 'reject';
13352 Roo.data.Record.COMMIT = 'commit';
13353
13354 Roo.data.Record.prototype = {
13355     /**
13356      * Readonly flag - true if this record has been modified.
13357      * @type Boolean
13358      */
13359     dirty : false,
13360     editing : false,
13361     error: null,
13362     modified: null,
13363
13364     // private
13365     join : function(store){
13366         this.store = store;
13367     },
13368
13369     /**
13370      * Set the named field to the specified value.
13371      * @param {String} name The name of the field to set.
13372      * @param {Object} value The value to set the field to.
13373      */
13374     set : function(name, value){
13375         if(this.data[name] == value){
13376             return;
13377         }
13378         this.dirty = true;
13379         if(!this.modified){
13380             this.modified = {};
13381         }
13382         if(typeof this.modified[name] == 'undefined'){
13383             this.modified[name] = this.data[name];
13384         }
13385         this.data[name] = value;
13386         if(!this.editing && this.store){
13387             this.store.afterEdit(this);
13388         }       
13389     },
13390
13391     /**
13392      * Get the value of the named field.
13393      * @param {String} name The name of the field to get the value of.
13394      * @return {Object} The value of the field.
13395      */
13396     get : function(name){
13397         return this.data[name]; 
13398     },
13399
13400     // private
13401     beginEdit : function(){
13402         this.editing = true;
13403         this.modified = {}; 
13404     },
13405
13406     // private
13407     cancelEdit : function(){
13408         this.editing = false;
13409         delete this.modified;
13410     },
13411
13412     // private
13413     endEdit : function(){
13414         this.editing = false;
13415         if(this.dirty && this.store){
13416             this.store.afterEdit(this);
13417         }
13418     },
13419
13420     /**
13421      * Usually called by the {@link Roo.data.Store} which owns the Record.
13422      * Rejects all changes made to the Record since either creation, or the last commit operation.
13423      * Modified fields are reverted to their original values.
13424      * <p>
13425      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426      * of reject operations.
13427      */
13428     reject : function(){
13429         var m = this.modified;
13430         for(var n in m){
13431             if(typeof m[n] != "function"){
13432                 this.data[n] = m[n];
13433             }
13434         }
13435         this.dirty = false;
13436         delete this.modified;
13437         this.editing = false;
13438         if(this.store){
13439             this.store.afterReject(this);
13440         }
13441     },
13442
13443     /**
13444      * Usually called by the {@link Roo.data.Store} which owns the Record.
13445      * Commits all changes made to the Record since either creation, or the last commit operation.
13446      * <p>
13447      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13448      * of commit operations.
13449      */
13450     commit : function(){
13451         this.dirty = false;
13452         delete this.modified;
13453         this.editing = false;
13454         if(this.store){
13455             this.store.afterCommit(this);
13456         }
13457     },
13458
13459     // private
13460     hasError : function(){
13461         return this.error != null;
13462     },
13463
13464     // private
13465     clearError : function(){
13466         this.error = null;
13467     },
13468
13469     /**
13470      * Creates a copy of this record.
13471      * @param {String} id (optional) A new record id if you don't want to use this record's id
13472      * @return {Record}
13473      */
13474     copy : function(newId) {
13475         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13476     }
13477 };/*
13478  * Based on:
13479  * Ext JS Library 1.1.1
13480  * Copyright(c) 2006-2007, Ext JS, LLC.
13481  *
13482  * Originally Released Under LGPL - original licence link has changed is not relivant.
13483  *
13484  * Fork - LGPL
13485  * <script type="text/javascript">
13486  */
13487
13488
13489
13490 /**
13491  * @class Roo.data.Store
13492  * @extends Roo.util.Observable
13493  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13494  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13495  * <p>
13496  * 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
13497  * has no knowledge of the format of the data returned by the Proxy.<br>
13498  * <p>
13499  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13500  * instances from the data object. These records are cached and made available through accessor functions.
13501  * @constructor
13502  * Creates a new Store.
13503  * @param {Object} config A config object containing the objects needed for the Store to access data,
13504  * and read the data into Records.
13505  */
13506 Roo.data.Store = function(config){
13507     this.data = new Roo.util.MixedCollection(false);
13508     this.data.getKey = function(o){
13509         return o.id;
13510     };
13511     this.baseParams = {};
13512     // private
13513     this.paramNames = {
13514         "start" : "start",
13515         "limit" : "limit",
13516         "sort" : "sort",
13517         "dir" : "dir",
13518         "multisort" : "_multisort"
13519     };
13520
13521     if(config && config.data){
13522         this.inlineData = config.data;
13523         delete config.data;
13524     }
13525
13526     Roo.apply(this, config);
13527     
13528     if(this.reader){ // reader passed
13529         this.reader = Roo.factory(this.reader, Roo.data);
13530         this.reader.xmodule = this.xmodule || false;
13531         if(!this.recordType){
13532             this.recordType = this.reader.recordType;
13533         }
13534         if(this.reader.onMetaChange){
13535             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13536         }
13537     }
13538
13539     if(this.recordType){
13540         this.fields = this.recordType.prototype.fields;
13541     }
13542     this.modified = [];
13543
13544     this.addEvents({
13545         /**
13546          * @event datachanged
13547          * Fires when the data cache has changed, and a widget which is using this Store
13548          * as a Record cache should refresh its view.
13549          * @param {Store} this
13550          */
13551         datachanged : true,
13552         /**
13553          * @event metachange
13554          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13555          * @param {Store} this
13556          * @param {Object} meta The JSON metadata
13557          */
13558         metachange : true,
13559         /**
13560          * @event add
13561          * Fires when Records have been added to the Store
13562          * @param {Store} this
13563          * @param {Roo.data.Record[]} records The array of Records added
13564          * @param {Number} index The index at which the record(s) were added
13565          */
13566         add : true,
13567         /**
13568          * @event remove
13569          * Fires when a Record has been removed from the Store
13570          * @param {Store} this
13571          * @param {Roo.data.Record} record The Record that was removed
13572          * @param {Number} index The index at which the record was removed
13573          */
13574         remove : true,
13575         /**
13576          * @event update
13577          * Fires when a Record has been updated
13578          * @param {Store} this
13579          * @param {Roo.data.Record} record The Record that was updated
13580          * @param {String} operation The update operation being performed.  Value may be one of:
13581          * <pre><code>
13582  Roo.data.Record.EDIT
13583  Roo.data.Record.REJECT
13584  Roo.data.Record.COMMIT
13585          * </code></pre>
13586          */
13587         update : true,
13588         /**
13589          * @event clear
13590          * Fires when the data cache has been cleared.
13591          * @param {Store} this
13592          */
13593         clear : true,
13594         /**
13595          * @event beforeload
13596          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13597          * the load action will be canceled.
13598          * @param {Store} this
13599          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13600          */
13601         beforeload : true,
13602         /**
13603          * @event beforeloadadd
13604          * Fires after a new set of Records has been loaded.
13605          * @param {Store} this
13606          * @param {Roo.data.Record[]} records The Records that were loaded
13607          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13608          */
13609         beforeloadadd : true,
13610         /**
13611          * @event load
13612          * Fires after a new set of Records has been loaded, before they are added to the store.
13613          * @param {Store} this
13614          * @param {Roo.data.Record[]} records The Records that were loaded
13615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616          * @params {Object} return from reader
13617          */
13618         load : true,
13619         /**
13620          * @event loadexception
13621          * Fires if an exception occurs in the Proxy during loading.
13622          * Called with the signature of the Proxy's "loadexception" event.
13623          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13624          * 
13625          * @param {Proxy} 
13626          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13627          * @param {Object} load options 
13628          * @param {Object} jsonData from your request (normally this contains the Exception)
13629          */
13630         loadexception : true
13631     });
13632     
13633     if(this.proxy){
13634         this.proxy = Roo.factory(this.proxy, Roo.data);
13635         this.proxy.xmodule = this.xmodule || false;
13636         this.relayEvents(this.proxy,  ["loadexception"]);
13637     }
13638     this.sortToggle = {};
13639     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13640
13641     Roo.data.Store.superclass.constructor.call(this);
13642
13643     if(this.inlineData){
13644         this.loadData(this.inlineData);
13645         delete this.inlineData;
13646     }
13647 };
13648
13649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13650      /**
13651     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13652     * without a remote query - used by combo/forms at present.
13653     */
13654     
13655     /**
13656     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13657     */
13658     /**
13659     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13660     */
13661     /**
13662     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13663     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13664     */
13665     /**
13666     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13667     * on any HTTP request
13668     */
13669     /**
13670     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13671     */
13672     /**
13673     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13674     */
13675     multiSort: false,
13676     /**
13677     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13678     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13679     */
13680     remoteSort : false,
13681
13682     /**
13683     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13684      * loaded or when a record is removed. (defaults to false).
13685     */
13686     pruneModifiedRecords : false,
13687
13688     // private
13689     lastOptions : null,
13690
13691     /**
13692      * Add Records to the Store and fires the add event.
13693      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13694      */
13695     add : function(records){
13696         records = [].concat(records);
13697         for(var i = 0, len = records.length; i < len; i++){
13698             records[i].join(this);
13699         }
13700         var index = this.data.length;
13701         this.data.addAll(records);
13702         this.fireEvent("add", this, records, index);
13703     },
13704
13705     /**
13706      * Remove a Record from the Store and fires the remove event.
13707      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13708      */
13709     remove : function(record){
13710         var index = this.data.indexOf(record);
13711         this.data.removeAt(index);
13712  
13713         if(this.pruneModifiedRecords){
13714             this.modified.remove(record);
13715         }
13716         this.fireEvent("remove", this, record, index);
13717     },
13718
13719     /**
13720      * Remove all Records from the Store and fires the clear event.
13721      */
13722     removeAll : function(){
13723         this.data.clear();
13724         if(this.pruneModifiedRecords){
13725             this.modified = [];
13726         }
13727         this.fireEvent("clear", this);
13728     },
13729
13730     /**
13731      * Inserts Records to the Store at the given index and fires the add event.
13732      * @param {Number} index The start index at which to insert the passed Records.
13733      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13734      */
13735     insert : function(index, records){
13736         records = [].concat(records);
13737         for(var i = 0, len = records.length; i < len; i++){
13738             this.data.insert(index, records[i]);
13739             records[i].join(this);
13740         }
13741         this.fireEvent("add", this, records, index);
13742     },
13743
13744     /**
13745      * Get the index within the cache of the passed Record.
13746      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13747      * @return {Number} The index of the passed Record. Returns -1 if not found.
13748      */
13749     indexOf : function(record){
13750         return this.data.indexOf(record);
13751     },
13752
13753     /**
13754      * Get the index within the cache of the Record with the passed id.
13755      * @param {String} id The id of the Record to find.
13756      * @return {Number} The index of the Record. Returns -1 if not found.
13757      */
13758     indexOfId : function(id){
13759         return this.data.indexOfKey(id);
13760     },
13761
13762     /**
13763      * Get the Record with the specified id.
13764      * @param {String} id The id of the Record to find.
13765      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13766      */
13767     getById : function(id){
13768         return this.data.key(id);
13769     },
13770
13771     /**
13772      * Get the Record at the specified index.
13773      * @param {Number} index The index of the Record to find.
13774      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13775      */
13776     getAt : function(index){
13777         return this.data.itemAt(index);
13778     },
13779
13780     /**
13781      * Returns a range of Records between specified indices.
13782      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13783      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13784      * @return {Roo.data.Record[]} An array of Records
13785      */
13786     getRange : function(start, end){
13787         return this.data.getRange(start, end);
13788     },
13789
13790     // private
13791     storeOptions : function(o){
13792         o = Roo.apply({}, o);
13793         delete o.callback;
13794         delete o.scope;
13795         this.lastOptions = o;
13796     },
13797
13798     /**
13799      * Loads the Record cache from the configured Proxy using the configured Reader.
13800      * <p>
13801      * If using remote paging, then the first load call must specify the <em>start</em>
13802      * and <em>limit</em> properties in the options.params property to establish the initial
13803      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13804      * <p>
13805      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13806      * and this call will return before the new data has been loaded. Perform any post-processing
13807      * in a callback function, or in a "load" event handler.</strong>
13808      * <p>
13809      * @param {Object} options An object containing properties which control loading options:<ul>
13810      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13811      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13812      * passed the following arguments:<ul>
13813      * <li>r : Roo.data.Record[]</li>
13814      * <li>options: Options object from the load call</li>
13815      * <li>success: Boolean success indicator</li></ul></li>
13816      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13817      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13818      * </ul>
13819      */
13820     load : function(options){
13821         options = options || {};
13822         if(this.fireEvent("beforeload", this, options) !== false){
13823             this.storeOptions(options);
13824             var p = Roo.apply(options.params || {}, this.baseParams);
13825             // if meta was not loaded from remote source.. try requesting it.
13826             if (!this.reader.metaFromRemote) {
13827                 p._requestMeta = 1;
13828             }
13829             if(this.sortInfo && this.remoteSort){
13830                 var pn = this.paramNames;
13831                 p[pn["sort"]] = this.sortInfo.field;
13832                 p[pn["dir"]] = this.sortInfo.direction;
13833             }
13834             if (this.multiSort) {
13835                 var pn = this.paramNames;
13836                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13837             }
13838             
13839             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13840         }
13841     },
13842
13843     /**
13844      * Reloads the Record cache from the configured Proxy using the configured Reader and
13845      * the options from the last load operation performed.
13846      * @param {Object} options (optional) An object containing properties which may override the options
13847      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13848      * the most recently used options are reused).
13849      */
13850     reload : function(options){
13851         this.load(Roo.applyIf(options||{}, this.lastOptions));
13852     },
13853
13854     // private
13855     // Called as a callback by the Reader during a load operation.
13856     loadRecords : function(o, options, success){
13857         if(!o || success === false){
13858             if(success !== false){
13859                 this.fireEvent("load", this, [], options, o);
13860             }
13861             if(options.callback){
13862                 options.callback.call(options.scope || this, [], options, false);
13863             }
13864             return;
13865         }
13866         // if data returned failure - throw an exception.
13867         if (o.success === false) {
13868             // show a message if no listener is registered.
13869             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13870                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13871             }
13872             // loadmask wil be hooked into this..
13873             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13874             return;
13875         }
13876         var r = o.records, t = o.totalRecords || r.length;
13877         
13878         this.fireEvent("beforeloadadd", this, r, options, o);
13879         
13880         if(!options || options.add !== true){
13881             if(this.pruneModifiedRecords){
13882                 this.modified = [];
13883             }
13884             for(var i = 0, len = r.length; i < len; i++){
13885                 r[i].join(this);
13886             }
13887             if(this.snapshot){
13888                 this.data = this.snapshot;
13889                 delete this.snapshot;
13890             }
13891             this.data.clear();
13892             this.data.addAll(r);
13893             this.totalLength = t;
13894             this.applySort();
13895             this.fireEvent("datachanged", this);
13896         }else{
13897             this.totalLength = Math.max(t, this.data.length+r.length);
13898             this.add(r);
13899         }
13900         
13901         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13902                 
13903             var e = new Roo.data.Record({});
13904
13905             e.set(this.parent.displayField, this.parent.emptyTitle);
13906             e.set(this.parent.valueField, '');
13907
13908             this.insert(0, e);
13909         }
13910             
13911         this.fireEvent("load", this, r, options, o);
13912         if(options.callback){
13913             options.callback.call(options.scope || this, r, options, true);
13914         }
13915     },
13916
13917
13918     /**
13919      * Loads data from a passed data block. A Reader which understands the format of the data
13920      * must have been configured in the constructor.
13921      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13922      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13923      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13924      */
13925     loadData : function(o, append){
13926         var r = this.reader.readRecords(o);
13927         this.loadRecords(r, {add: append}, true);
13928     },
13929     
13930      /**
13931      * using 'cn' the nested child reader read the child array into it's child stores.
13932      * @param {Object} rec The record with a 'children array
13933      */
13934     loadDataFromChildren : function(rec)
13935     {
13936         this.loadData(this.reader.toLoadData(rec));
13937     },
13938     
13939
13940     /**
13941      * Gets the number of cached records.
13942      * <p>
13943      * <em>If using paging, this may not be the total size of the dataset. If the data object
13944      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13945      * the data set size</em>
13946      */
13947     getCount : function(){
13948         return this.data.length || 0;
13949     },
13950
13951     /**
13952      * Gets the total number of records in the dataset as returned by the server.
13953      * <p>
13954      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13955      * the dataset size</em>
13956      */
13957     getTotalCount : function(){
13958         return this.totalLength || 0;
13959     },
13960
13961     /**
13962      * Returns the sort state of the Store as an object with two properties:
13963      * <pre><code>
13964  field {String} The name of the field by which the Records are sorted
13965  direction {String} The sort order, "ASC" or "DESC"
13966      * </code></pre>
13967      */
13968     getSortState : function(){
13969         return this.sortInfo;
13970     },
13971
13972     // private
13973     applySort : function(){
13974         if(this.sortInfo && !this.remoteSort){
13975             var s = this.sortInfo, f = s.field;
13976             var st = this.fields.get(f).sortType;
13977             var fn = function(r1, r2){
13978                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13979                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13980             };
13981             this.data.sort(s.direction, fn);
13982             if(this.snapshot && this.snapshot != this.data){
13983                 this.snapshot.sort(s.direction, fn);
13984             }
13985         }
13986     },
13987
13988     /**
13989      * Sets the default sort column and order to be used by the next load operation.
13990      * @param {String} fieldName The name of the field to sort by.
13991      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13992      */
13993     setDefaultSort : function(field, dir){
13994         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13995     },
13996
13997     /**
13998      * Sort the Records.
13999      * If remote sorting is used, the sort is performed on the server, and the cache is
14000      * reloaded. If local sorting is used, the cache is sorted internally.
14001      * @param {String} fieldName The name of the field to sort by.
14002      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14003      */
14004     sort : function(fieldName, dir){
14005         var f = this.fields.get(fieldName);
14006         if(!dir){
14007             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14008             
14009             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14010                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14011             }else{
14012                 dir = f.sortDir;
14013             }
14014         }
14015         this.sortToggle[f.name] = dir;
14016         this.sortInfo = {field: f.name, direction: dir};
14017         if(!this.remoteSort){
14018             this.applySort();
14019             this.fireEvent("datachanged", this);
14020         }else{
14021             this.load(this.lastOptions);
14022         }
14023     },
14024
14025     /**
14026      * Calls the specified function for each of the Records in the cache.
14027      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14028      * Returning <em>false</em> aborts and exits the iteration.
14029      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14030      */
14031     each : function(fn, scope){
14032         this.data.each(fn, scope);
14033     },
14034
14035     /**
14036      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14037      * (e.g., during paging).
14038      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14039      */
14040     getModifiedRecords : function(){
14041         return this.modified;
14042     },
14043
14044     // private
14045     createFilterFn : function(property, value, anyMatch){
14046         if(!value.exec){ // not a regex
14047             value = String(value);
14048             if(value.length == 0){
14049                 return false;
14050             }
14051             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14052         }
14053         return function(r){
14054             return value.test(r.data[property]);
14055         };
14056     },
14057
14058     /**
14059      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14060      * @param {String} property A field on your records
14061      * @param {Number} start The record index to start at (defaults to 0)
14062      * @param {Number} end The last record index to include (defaults to length - 1)
14063      * @return {Number} The sum
14064      */
14065     sum : function(property, start, end){
14066         var rs = this.data.items, v = 0;
14067         start = start || 0;
14068         end = (end || end === 0) ? end : rs.length-1;
14069
14070         for(var i = start; i <= end; i++){
14071             v += (rs[i].data[property] || 0);
14072         }
14073         return v;
14074     },
14075
14076     /**
14077      * Filter the records by a specified property.
14078      * @param {String} field A field on your records
14079      * @param {String/RegExp} value Either a string that the field
14080      * should start with or a RegExp to test against the field
14081      * @param {Boolean} anyMatch True to match any part not just the beginning
14082      */
14083     filter : function(property, value, anyMatch){
14084         var fn = this.createFilterFn(property, value, anyMatch);
14085         return fn ? this.filterBy(fn) : this.clearFilter();
14086     },
14087
14088     /**
14089      * Filter by a function. The specified function will be called with each
14090      * record in this data source. If the function returns true the record is included,
14091      * otherwise it is filtered.
14092      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14093      * @param {Object} scope (optional) The scope of the function (defaults to this)
14094      */
14095     filterBy : function(fn, scope){
14096         this.snapshot = this.snapshot || this.data;
14097         this.data = this.queryBy(fn, scope||this);
14098         this.fireEvent("datachanged", this);
14099     },
14100
14101     /**
14102      * Query the records by a specified property.
14103      * @param {String} field A field on your records
14104      * @param {String/RegExp} value Either a string that the field
14105      * should start with or a RegExp to test against the field
14106      * @param {Boolean} anyMatch True to match any part not just the beginning
14107      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14108      */
14109     query : function(property, value, anyMatch){
14110         var fn = this.createFilterFn(property, value, anyMatch);
14111         return fn ? this.queryBy(fn) : this.data.clone();
14112     },
14113
14114     /**
14115      * Query by a function. The specified function will be called with each
14116      * record in this data source. If the function returns true the record is included
14117      * in the results.
14118      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14119      * @param {Object} scope (optional) The scope of the function (defaults to this)
14120       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14121      **/
14122     queryBy : function(fn, scope){
14123         var data = this.snapshot || this.data;
14124         return data.filterBy(fn, scope||this);
14125     },
14126
14127     /**
14128      * Collects unique values for a particular dataIndex from this store.
14129      * @param {String} dataIndex The property to collect
14130      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14131      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14132      * @return {Array} An array of the unique values
14133      **/
14134     collect : function(dataIndex, allowNull, bypassFilter){
14135         var d = (bypassFilter === true && this.snapshot) ?
14136                 this.snapshot.items : this.data.items;
14137         var v, sv, r = [], l = {};
14138         for(var i = 0, len = d.length; i < len; i++){
14139             v = d[i].data[dataIndex];
14140             sv = String(v);
14141             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14142                 l[sv] = true;
14143                 r[r.length] = v;
14144             }
14145         }
14146         return r;
14147     },
14148
14149     /**
14150      * Revert to a view of the Record cache with no filtering applied.
14151      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14152      */
14153     clearFilter : function(suppressEvent){
14154         if(this.snapshot && this.snapshot != this.data){
14155             this.data = this.snapshot;
14156             delete this.snapshot;
14157             if(suppressEvent !== true){
14158                 this.fireEvent("datachanged", this);
14159             }
14160         }
14161     },
14162
14163     // private
14164     afterEdit : function(record){
14165         if(this.modified.indexOf(record) == -1){
14166             this.modified.push(record);
14167         }
14168         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14169     },
14170     
14171     // private
14172     afterReject : function(record){
14173         this.modified.remove(record);
14174         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14175     },
14176
14177     // private
14178     afterCommit : function(record){
14179         this.modified.remove(record);
14180         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14181     },
14182
14183     /**
14184      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14185      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14186      */
14187     commitChanges : function(){
14188         var m = this.modified.slice(0);
14189         this.modified = [];
14190         for(var i = 0, len = m.length; i < len; i++){
14191             m[i].commit();
14192         }
14193     },
14194
14195     /**
14196      * Cancel outstanding changes on all changed records.
14197      */
14198     rejectChanges : function(){
14199         var m = this.modified.slice(0);
14200         this.modified = [];
14201         for(var i = 0, len = m.length; i < len; i++){
14202             m[i].reject();
14203         }
14204     },
14205
14206     onMetaChange : function(meta, rtype, o){
14207         this.recordType = rtype;
14208         this.fields = rtype.prototype.fields;
14209         delete this.snapshot;
14210         this.sortInfo = meta.sortInfo || this.sortInfo;
14211         this.modified = [];
14212         this.fireEvent('metachange', this, this.reader.meta);
14213     },
14214     
14215     moveIndex : function(data, type)
14216     {
14217         var index = this.indexOf(data);
14218         
14219         var newIndex = index + type;
14220         
14221         this.remove(data);
14222         
14223         this.insert(newIndex, data);
14224         
14225     }
14226 });/*
14227  * Based on:
14228  * Ext JS Library 1.1.1
14229  * Copyright(c) 2006-2007, Ext JS, LLC.
14230  *
14231  * Originally Released Under LGPL - original licence link has changed is not relivant.
14232  *
14233  * Fork - LGPL
14234  * <script type="text/javascript">
14235  */
14236
14237 /**
14238  * @class Roo.data.SimpleStore
14239  * @extends Roo.data.Store
14240  * Small helper class to make creating Stores from Array data easier.
14241  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14242  * @cfg {Array} fields An array of field definition objects, or field name strings.
14243  * @cfg {Object} an existing reader (eg. copied from another store)
14244  * @cfg {Array} data The multi-dimensional array of data
14245  * @constructor
14246  * @param {Object} config
14247  */
14248 Roo.data.SimpleStore = function(config)
14249 {
14250     Roo.data.SimpleStore.superclass.constructor.call(this, {
14251         isLocal : true,
14252         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14253                 id: config.id
14254             },
14255             Roo.data.Record.create(config.fields)
14256         ),
14257         proxy : new Roo.data.MemoryProxy(config.data)
14258     });
14259     this.load();
14260 };
14261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14262  * Based on:
14263  * Ext JS Library 1.1.1
14264  * Copyright(c) 2006-2007, Ext JS, LLC.
14265  *
14266  * Originally Released Under LGPL - original licence link has changed is not relivant.
14267  *
14268  * Fork - LGPL
14269  * <script type="text/javascript">
14270  */
14271
14272 /**
14273 /**
14274  * @extends Roo.data.Store
14275  * @class Roo.data.JsonStore
14276  * Small helper class to make creating Stores for JSON data easier. <br/>
14277 <pre><code>
14278 var store = new Roo.data.JsonStore({
14279     url: 'get-images.php',
14280     root: 'images',
14281     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14282 });
14283 </code></pre>
14284  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14285  * JsonReader and HttpProxy (unless inline data is provided).</b>
14286  * @cfg {Array} fields An array of field definition objects, or field name strings.
14287  * @constructor
14288  * @param {Object} config
14289  */
14290 Roo.data.JsonStore = function(c){
14291     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14292         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14293         reader: new Roo.data.JsonReader(c, c.fields)
14294     }));
14295 };
14296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14297  * Based on:
14298  * Ext JS Library 1.1.1
14299  * Copyright(c) 2006-2007, Ext JS, LLC.
14300  *
14301  * Originally Released Under LGPL - original licence link has changed is not relivant.
14302  *
14303  * Fork - LGPL
14304  * <script type="text/javascript">
14305  */
14306
14307  
14308 Roo.data.Field = function(config){
14309     if(typeof config == "string"){
14310         config = {name: config};
14311     }
14312     Roo.apply(this, config);
14313     
14314     if(!this.type){
14315         this.type = "auto";
14316     }
14317     
14318     var st = Roo.data.SortTypes;
14319     // named sortTypes are supported, here we look them up
14320     if(typeof this.sortType == "string"){
14321         this.sortType = st[this.sortType];
14322     }
14323     
14324     // set default sortType for strings and dates
14325     if(!this.sortType){
14326         switch(this.type){
14327             case "string":
14328                 this.sortType = st.asUCString;
14329                 break;
14330             case "date":
14331                 this.sortType = st.asDate;
14332                 break;
14333             default:
14334                 this.sortType = st.none;
14335         }
14336     }
14337
14338     // define once
14339     var stripRe = /[\$,%]/g;
14340
14341     // prebuilt conversion function for this field, instead of
14342     // switching every time we're reading a value
14343     if(!this.convert){
14344         var cv, dateFormat = this.dateFormat;
14345         switch(this.type){
14346             case "":
14347             case "auto":
14348             case undefined:
14349                 cv = function(v){ return v; };
14350                 break;
14351             case "string":
14352                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14353                 break;
14354             case "int":
14355                 cv = function(v){
14356                     return v !== undefined && v !== null && v !== '' ?
14357                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14358                     };
14359                 break;
14360             case "float":
14361                 cv = function(v){
14362                     return v !== undefined && v !== null && v !== '' ?
14363                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14364                     };
14365                 break;
14366             case "bool":
14367             case "boolean":
14368                 cv = function(v){ return v === true || v === "true" || v == 1; };
14369                 break;
14370             case "date":
14371                 cv = function(v){
14372                     if(!v){
14373                         return '';
14374                     }
14375                     if(v instanceof Date){
14376                         return v;
14377                     }
14378                     if(dateFormat){
14379                         if(dateFormat == "timestamp"){
14380                             return new Date(v*1000);
14381                         }
14382                         return Date.parseDate(v, dateFormat);
14383                     }
14384                     var parsed = Date.parse(v);
14385                     return parsed ? new Date(parsed) : null;
14386                 };
14387              break;
14388             
14389         }
14390         this.convert = cv;
14391     }
14392 };
14393
14394 Roo.data.Field.prototype = {
14395     dateFormat: null,
14396     defaultValue: "",
14397     mapping: null,
14398     sortType : null,
14399     sortDir : "ASC"
14400 };/*
14401  * Based on:
14402  * Ext JS Library 1.1.1
14403  * Copyright(c) 2006-2007, Ext JS, LLC.
14404  *
14405  * Originally Released Under LGPL - original licence link has changed is not relivant.
14406  *
14407  * Fork - LGPL
14408  * <script type="text/javascript">
14409  */
14410  
14411 // Base class for reading structured data from a data source.  This class is intended to be
14412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14413
14414 /**
14415  * @class Roo.data.DataReader
14416  * Base class for reading structured data from a data source.  This class is intended to be
14417  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14418  */
14419
14420 Roo.data.DataReader = function(meta, recordType){
14421     
14422     this.meta = meta;
14423     
14424     this.recordType = recordType instanceof Array ? 
14425         Roo.data.Record.create(recordType) : recordType;
14426 };
14427
14428 Roo.data.DataReader.prototype = {
14429     
14430     
14431     readerType : 'Data',
14432      /**
14433      * Create an empty record
14434      * @param {Object} data (optional) - overlay some values
14435      * @return {Roo.data.Record} record created.
14436      */
14437     newRow :  function(d) {
14438         var da =  {};
14439         this.recordType.prototype.fields.each(function(c) {
14440             switch( c.type) {
14441                 case 'int' : da[c.name] = 0; break;
14442                 case 'date' : da[c.name] = new Date(); break;
14443                 case 'float' : da[c.name] = 0.0; break;
14444                 case 'boolean' : da[c.name] = false; break;
14445                 default : da[c.name] = ""; break;
14446             }
14447             
14448         });
14449         return new this.recordType(Roo.apply(da, d));
14450     }
14451     
14452     
14453 };/*
14454  * Based on:
14455  * Ext JS Library 1.1.1
14456  * Copyright(c) 2006-2007, Ext JS, LLC.
14457  *
14458  * Originally Released Under LGPL - original licence link has changed is not relivant.
14459  *
14460  * Fork - LGPL
14461  * <script type="text/javascript">
14462  */
14463
14464 /**
14465  * @class Roo.data.DataProxy
14466  * @extends Roo.data.Observable
14467  * This class is an abstract base class for implementations which provide retrieval of
14468  * unformatted data objects.<br>
14469  * <p>
14470  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14471  * (of the appropriate type which knows how to parse the data object) to provide a block of
14472  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14473  * <p>
14474  * Custom implementations must implement the load method as described in
14475  * {@link Roo.data.HttpProxy#load}.
14476  */
14477 Roo.data.DataProxy = function(){
14478     this.addEvents({
14479         /**
14480          * @event beforeload
14481          * Fires before a network request is made to retrieve a data object.
14482          * @param {Object} This DataProxy object.
14483          * @param {Object} params The params parameter to the load function.
14484          */
14485         beforeload : true,
14486         /**
14487          * @event load
14488          * Fires before the load method's callback is called.
14489          * @param {Object} This DataProxy object.
14490          * @param {Object} o The data object.
14491          * @param {Object} arg The callback argument object passed to the load function.
14492          */
14493         load : true,
14494         /**
14495          * @event loadexception
14496          * Fires if an Exception occurs during data retrieval.
14497          * @param {Object} This DataProxy object.
14498          * @param {Object} o The data object.
14499          * @param {Object} arg The callback argument object passed to the load function.
14500          * @param {Object} e The Exception.
14501          */
14502         loadexception : true
14503     });
14504     Roo.data.DataProxy.superclass.constructor.call(this);
14505 };
14506
14507 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14508
14509     /**
14510      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14511      */
14512 /*
14513  * Based on:
14514  * Ext JS Library 1.1.1
14515  * Copyright(c) 2006-2007, Ext JS, LLC.
14516  *
14517  * Originally Released Under LGPL - original licence link has changed is not relivant.
14518  *
14519  * Fork - LGPL
14520  * <script type="text/javascript">
14521  */
14522 /**
14523  * @class Roo.data.MemoryProxy
14524  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14525  * to the Reader when its load method is called.
14526  * @constructor
14527  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14528  */
14529 Roo.data.MemoryProxy = function(data){
14530     if (data.data) {
14531         data = data.data;
14532     }
14533     Roo.data.MemoryProxy.superclass.constructor.call(this);
14534     this.data = data;
14535 };
14536
14537 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14538     
14539     /**
14540      * Load data from the requested source (in this case an in-memory
14541      * data object passed to the constructor), read the data object into
14542      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14543      * process that block using the passed callback.
14544      * @param {Object} params This parameter is not used by the MemoryProxy class.
14545      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14546      * object into a block of Roo.data.Records.
14547      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14548      * The function must be passed <ul>
14549      * <li>The Record block object</li>
14550      * <li>The "arg" argument from the load function</li>
14551      * <li>A boolean success indicator</li>
14552      * </ul>
14553      * @param {Object} scope The scope in which to call the callback
14554      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14555      */
14556     load : function(params, reader, callback, scope, arg){
14557         params = params || {};
14558         var result;
14559         try {
14560             result = reader.readRecords(params.data ? params.data :this.data);
14561         }catch(e){
14562             this.fireEvent("loadexception", this, arg, null, e);
14563             callback.call(scope, null, arg, false);
14564             return;
14565         }
14566         callback.call(scope, result, arg, true);
14567     },
14568     
14569     // private
14570     update : function(params, records){
14571         
14572     }
14573 });/*
14574  * Based on:
14575  * Ext JS Library 1.1.1
14576  * Copyright(c) 2006-2007, Ext JS, LLC.
14577  *
14578  * Originally Released Under LGPL - original licence link has changed is not relivant.
14579  *
14580  * Fork - LGPL
14581  * <script type="text/javascript">
14582  */
14583 /**
14584  * @class Roo.data.HttpProxy
14585  * @extends Roo.data.DataProxy
14586  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14587  * configured to reference a certain URL.<br><br>
14588  * <p>
14589  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14590  * from which the running page was served.<br><br>
14591  * <p>
14592  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14593  * <p>
14594  * Be aware that to enable the browser to parse an XML document, the server must set
14595  * the Content-Type header in the HTTP response to "text/xml".
14596  * @constructor
14597  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14598  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14599  * will be used to make the request.
14600  */
14601 Roo.data.HttpProxy = function(conn){
14602     Roo.data.HttpProxy.superclass.constructor.call(this);
14603     // is conn a conn config or a real conn?
14604     this.conn = conn;
14605     this.useAjax = !conn || !conn.events;
14606   
14607 };
14608
14609 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14610     // thse are take from connection...
14611     
14612     /**
14613      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14614      */
14615     /**
14616      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14617      * extra parameters to each request made by this object. (defaults to undefined)
14618      */
14619     /**
14620      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14621      *  to each request made by this object. (defaults to undefined)
14622      */
14623     /**
14624      * @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)
14625      */
14626     /**
14627      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14628      */
14629      /**
14630      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14631      * @type Boolean
14632      */
14633   
14634
14635     /**
14636      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14637      * @type Boolean
14638      */
14639     /**
14640      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14641      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14642      * a finer-grained basis than the DataProxy events.
14643      */
14644     getConnection : function(){
14645         return this.useAjax ? Roo.Ajax : this.conn;
14646     },
14647
14648     /**
14649      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14650      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14651      * process that block using the passed callback.
14652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14653      * for the request to the remote server.
14654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14655      * object into a block of Roo.data.Records.
14656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14657      * The function must be passed <ul>
14658      * <li>The Record block object</li>
14659      * <li>The "arg" argument from the load function</li>
14660      * <li>A boolean success indicator</li>
14661      * </ul>
14662      * @param {Object} scope The scope in which to call the callback
14663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14664      */
14665     load : function(params, reader, callback, scope, arg){
14666         if(this.fireEvent("beforeload", this, params) !== false){
14667             var  o = {
14668                 params : params || {},
14669                 request: {
14670                     callback : callback,
14671                     scope : scope,
14672                     arg : arg
14673                 },
14674                 reader: reader,
14675                 callback : this.loadResponse,
14676                 scope: this
14677             };
14678             if(this.useAjax){
14679                 Roo.applyIf(o, this.conn);
14680                 if(this.activeRequest){
14681                     Roo.Ajax.abort(this.activeRequest);
14682                 }
14683                 this.activeRequest = Roo.Ajax.request(o);
14684             }else{
14685                 this.conn.request(o);
14686             }
14687         }else{
14688             callback.call(scope||this, null, arg, false);
14689         }
14690     },
14691
14692     // private
14693     loadResponse : function(o, success, response){
14694         delete this.activeRequest;
14695         if(!success){
14696             this.fireEvent("loadexception", this, o, response);
14697             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14698             return;
14699         }
14700         var result;
14701         try {
14702             result = o.reader.read(response);
14703         }catch(e){
14704             this.fireEvent("loadexception", this, o, response, e);
14705             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14706             return;
14707         }
14708         
14709         this.fireEvent("load", this, o, o.request.arg);
14710         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14711     },
14712
14713     // private
14714     update : function(dataSet){
14715
14716     },
14717
14718     // private
14719     updateResponse : function(dataSet){
14720
14721     }
14722 });/*
14723  * Based on:
14724  * Ext JS Library 1.1.1
14725  * Copyright(c) 2006-2007, Ext JS, LLC.
14726  *
14727  * Originally Released Under LGPL - original licence link has changed is not relivant.
14728  *
14729  * Fork - LGPL
14730  * <script type="text/javascript">
14731  */
14732
14733 /**
14734  * @class Roo.data.ScriptTagProxy
14735  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14736  * other than the originating domain of the running page.<br><br>
14737  * <p>
14738  * <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
14739  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14740  * <p>
14741  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14742  * source code that is used as the source inside a &lt;script> tag.<br><br>
14743  * <p>
14744  * In order for the browser to process the returned data, the server must wrap the data object
14745  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14746  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14747  * depending on whether the callback name was passed:
14748  * <p>
14749  * <pre><code>
14750 boolean scriptTag = false;
14751 String cb = request.getParameter("callback");
14752 if (cb != null) {
14753     scriptTag = true;
14754     response.setContentType("text/javascript");
14755 } else {
14756     response.setContentType("application/x-json");
14757 }
14758 Writer out = response.getWriter();
14759 if (scriptTag) {
14760     out.write(cb + "(");
14761 }
14762 out.print(dataBlock.toJsonString());
14763 if (scriptTag) {
14764     out.write(");");
14765 }
14766 </pre></code>
14767  *
14768  * @constructor
14769  * @param {Object} config A configuration object.
14770  */
14771 Roo.data.ScriptTagProxy = function(config){
14772     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14773     Roo.apply(this, config);
14774     this.head = document.getElementsByTagName("head")[0];
14775 };
14776
14777 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14778
14779 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14780     /**
14781      * @cfg {String} url The URL from which to request the data object.
14782      */
14783     /**
14784      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14785      */
14786     timeout : 30000,
14787     /**
14788      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14789      * the server the name of the callback function set up by the load call to process the returned data object.
14790      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14791      * javascript output which calls this named function passing the data object as its only parameter.
14792      */
14793     callbackParam : "callback",
14794     /**
14795      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14796      * name to the request.
14797      */
14798     nocache : true,
14799
14800     /**
14801      * Load data from the configured URL, read the data object into
14802      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14803      * process that block using the passed callback.
14804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14805      * for the request to the remote server.
14806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14807      * object into a block of Roo.data.Records.
14808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14809      * The function must be passed <ul>
14810      * <li>The Record block object</li>
14811      * <li>The "arg" argument from the load function</li>
14812      * <li>A boolean success indicator</li>
14813      * </ul>
14814      * @param {Object} scope The scope in which to call the callback
14815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14816      */
14817     load : function(params, reader, callback, scope, arg){
14818         if(this.fireEvent("beforeload", this, params) !== false){
14819
14820             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14821
14822             var url = this.url;
14823             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14824             if(this.nocache){
14825                 url += "&_dc=" + (new Date().getTime());
14826             }
14827             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14828             var trans = {
14829                 id : transId,
14830                 cb : "stcCallback"+transId,
14831                 scriptId : "stcScript"+transId,
14832                 params : params,
14833                 arg : arg,
14834                 url : url,
14835                 callback : callback,
14836                 scope : scope,
14837                 reader : reader
14838             };
14839             var conn = this;
14840
14841             window[trans.cb] = function(o){
14842                 conn.handleResponse(o, trans);
14843             };
14844
14845             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14846
14847             if(this.autoAbort !== false){
14848                 this.abort();
14849             }
14850
14851             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14852
14853             var script = document.createElement("script");
14854             script.setAttribute("src", url);
14855             script.setAttribute("type", "text/javascript");
14856             script.setAttribute("id", trans.scriptId);
14857             this.head.appendChild(script);
14858
14859             this.trans = trans;
14860         }else{
14861             callback.call(scope||this, null, arg, false);
14862         }
14863     },
14864
14865     // private
14866     isLoading : function(){
14867         return this.trans ? true : false;
14868     },
14869
14870     /**
14871      * Abort the current server request.
14872      */
14873     abort : function(){
14874         if(this.isLoading()){
14875             this.destroyTrans(this.trans);
14876         }
14877     },
14878
14879     // private
14880     destroyTrans : function(trans, isLoaded){
14881         this.head.removeChild(document.getElementById(trans.scriptId));
14882         clearTimeout(trans.timeoutId);
14883         if(isLoaded){
14884             window[trans.cb] = undefined;
14885             try{
14886                 delete window[trans.cb];
14887             }catch(e){}
14888         }else{
14889             // if hasn't been loaded, wait for load to remove it to prevent script error
14890             window[trans.cb] = function(){
14891                 window[trans.cb] = undefined;
14892                 try{
14893                     delete window[trans.cb];
14894                 }catch(e){}
14895             };
14896         }
14897     },
14898
14899     // private
14900     handleResponse : function(o, trans){
14901         this.trans = false;
14902         this.destroyTrans(trans, true);
14903         var result;
14904         try {
14905             result = trans.reader.readRecords(o);
14906         }catch(e){
14907             this.fireEvent("loadexception", this, o, trans.arg, e);
14908             trans.callback.call(trans.scope||window, null, trans.arg, false);
14909             return;
14910         }
14911         this.fireEvent("load", this, o, trans.arg);
14912         trans.callback.call(trans.scope||window, result, trans.arg, true);
14913     },
14914
14915     // private
14916     handleFailure : function(trans){
14917         this.trans = false;
14918         this.destroyTrans(trans, false);
14919         this.fireEvent("loadexception", this, null, trans.arg);
14920         trans.callback.call(trans.scope||window, null, trans.arg, false);
14921     }
14922 });/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932
14933 /**
14934  * @class Roo.data.JsonReader
14935  * @extends Roo.data.DataReader
14936  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14937  * based on mappings in a provided Roo.data.Record constructor.
14938  * 
14939  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14940  * in the reply previously. 
14941  * 
14942  * <p>
14943  * Example code:
14944  * <pre><code>
14945 var RecordDef = Roo.data.Record.create([
14946     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14947     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14948 ]);
14949 var myReader = new Roo.data.JsonReader({
14950     totalProperty: "results",    // The property which contains the total dataset size (optional)
14951     root: "rows",                // The property which contains an Array of row objects
14952     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14953 }, RecordDef);
14954 </code></pre>
14955  * <p>
14956  * This would consume a JSON file like this:
14957  * <pre><code>
14958 { 'results': 2, 'rows': [
14959     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14960     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14961 }
14962 </code></pre>
14963  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14964  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14965  * paged from the remote server.
14966  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14967  * @cfg {String} root name of the property which contains the Array of row objects.
14968  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14969  * @cfg {Array} fields Array of field definition objects
14970  * @constructor
14971  * Create a new JsonReader
14972  * @param {Object} meta Metadata configuration options
14973  * @param {Object} recordType Either an Array of field definition objects,
14974  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14975  */
14976 Roo.data.JsonReader = function(meta, recordType){
14977     
14978     meta = meta || {};
14979     // set some defaults:
14980     Roo.applyIf(meta, {
14981         totalProperty: 'total',
14982         successProperty : 'success',
14983         root : 'data',
14984         id : 'id'
14985     });
14986     
14987     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14988 };
14989 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14990     
14991     readerType : 'Json',
14992     
14993     /**
14994      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14995      * Used by Store query builder to append _requestMeta to params.
14996      * 
14997      */
14998     metaFromRemote : false,
14999     /**
15000      * This method is only used by a DataProxy which has retrieved data from a remote server.
15001      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15002      * @return {Object} data A data block which is used by an Roo.data.Store object as
15003      * a cache of Roo.data.Records.
15004      */
15005     read : function(response){
15006         var json = response.responseText;
15007        
15008         var o = /* eval:var:o */ eval("("+json+")");
15009         if(!o) {
15010             throw {message: "JsonReader.read: Json object not found"};
15011         }
15012         
15013         if(o.metaData){
15014             
15015             delete this.ef;
15016             this.metaFromRemote = true;
15017             this.meta = o.metaData;
15018             this.recordType = Roo.data.Record.create(o.metaData.fields);
15019             this.onMetaChange(this.meta, this.recordType, o);
15020         }
15021         return this.readRecords(o);
15022     },
15023
15024     // private function a store will implement
15025     onMetaChange : function(meta, recordType, o){
15026
15027     },
15028
15029     /**
15030          * @ignore
15031          */
15032     simpleAccess: function(obj, subsc) {
15033         return obj[subsc];
15034     },
15035
15036         /**
15037          * @ignore
15038          */
15039     getJsonAccessor: function(){
15040         var re = /[\[\.]/;
15041         return function(expr) {
15042             try {
15043                 return(re.test(expr))
15044                     ? new Function("obj", "return obj." + expr)
15045                     : function(obj){
15046                         return obj[expr];
15047                     };
15048             } catch(e){}
15049             return Roo.emptyFn;
15050         };
15051     }(),
15052
15053     /**
15054      * Create a data block containing Roo.data.Records from an XML document.
15055      * @param {Object} o An object which contains an Array of row objects in the property specified
15056      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15057      * which contains the total size of the dataset.
15058      * @return {Object} data A data block which is used by an Roo.data.Store object as
15059      * a cache of Roo.data.Records.
15060      */
15061     readRecords : function(o){
15062         /**
15063          * After any data loads, the raw JSON data is available for further custom processing.
15064          * @type Object
15065          */
15066         this.o = o;
15067         var s = this.meta, Record = this.recordType,
15068             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15069
15070 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15071         if (!this.ef) {
15072             if(s.totalProperty) {
15073                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15074                 }
15075                 if(s.successProperty) {
15076                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15077                 }
15078                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15079                 if (s.id) {
15080                         var g = this.getJsonAccessor(s.id);
15081                         this.getId = function(rec) {
15082                                 var r = g(rec);  
15083                                 return (r === undefined || r === "") ? null : r;
15084                         };
15085                 } else {
15086                         this.getId = function(){return null;};
15087                 }
15088             this.ef = [];
15089             for(var jj = 0; jj < fl; jj++){
15090                 f = fi[jj];
15091                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15092                 this.ef[jj] = this.getJsonAccessor(map);
15093             }
15094         }
15095
15096         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15097         if(s.totalProperty){
15098             var vt = parseInt(this.getTotal(o), 10);
15099             if(!isNaN(vt)){
15100                 totalRecords = vt;
15101             }
15102         }
15103         if(s.successProperty){
15104             var vs = this.getSuccess(o);
15105             if(vs === false || vs === 'false'){
15106                 success = false;
15107             }
15108         }
15109         var records = [];
15110         for(var i = 0; i < c; i++){
15111                 var n = root[i];
15112             var values = {};
15113             var id = this.getId(n);
15114             for(var j = 0; j < fl; j++){
15115                 f = fi[j];
15116             var v = this.ef[j](n);
15117             if (!f.convert) {
15118                 Roo.log('missing convert for ' + f.name);
15119                 Roo.log(f);
15120                 continue;
15121             }
15122             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15123             }
15124             var record = new Record(values, id);
15125             record.json = n;
15126             records[i] = record;
15127         }
15128         return {
15129             raw : o,
15130             success : success,
15131             records : records,
15132             totalRecords : totalRecords
15133         };
15134     },
15135     // used when loading children.. @see loadDataFromChildren
15136     toLoadData: function(rec)
15137     {
15138         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15139         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15140         return { data : data, total : data.length };
15141         
15142     }
15143 });/*
15144  * Based on:
15145  * Ext JS Library 1.1.1
15146  * Copyright(c) 2006-2007, Ext JS, LLC.
15147  *
15148  * Originally Released Under LGPL - original licence link has changed is not relivant.
15149  *
15150  * Fork - LGPL
15151  * <script type="text/javascript">
15152  */
15153
15154 /**
15155  * @class Roo.data.ArrayReader
15156  * @extends Roo.data.DataReader
15157  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15158  * Each element of that Array represents a row of data fields. The
15159  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15160  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15161  * <p>
15162  * Example code:.
15163  * <pre><code>
15164 var RecordDef = Roo.data.Record.create([
15165     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15166     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15167 ]);
15168 var myReader = new Roo.data.ArrayReader({
15169     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15170 }, RecordDef);
15171 </code></pre>
15172  * <p>
15173  * This would consume an Array like this:
15174  * <pre><code>
15175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15176   </code></pre>
15177  
15178  * @constructor
15179  * Create a new JsonReader
15180  * @param {Object} meta Metadata configuration options.
15181  * @param {Object|Array} recordType Either an Array of field definition objects
15182  * 
15183  * @cfg {Array} fields Array of field definition objects
15184  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15185  * as specified to {@link Roo.data.Record#create},
15186  * or an {@link Roo.data.Record} object
15187  *
15188  * 
15189  * created using {@link Roo.data.Record#create}.
15190  */
15191 Roo.data.ArrayReader = function(meta, recordType)
15192 {    
15193     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15194 };
15195
15196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15197     
15198       /**
15199      * Create a data block containing Roo.data.Records from an XML document.
15200      * @param {Object} o An Array of row objects which represents the dataset.
15201      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15202      * a cache of Roo.data.Records.
15203      */
15204     readRecords : function(o)
15205     {
15206         var sid = this.meta ? this.meta.id : null;
15207         var recordType = this.recordType, fields = recordType.prototype.fields;
15208         var records = [];
15209         var root = o;
15210         for(var i = 0; i < root.length; i++){
15211             var n = root[i];
15212             var values = {};
15213             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15214             for(var j = 0, jlen = fields.length; j < jlen; j++){
15215                 var f = fields.items[j];
15216                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15217                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15218                 v = f.convert(v);
15219                 values[f.name] = v;
15220             }
15221             var record = new recordType(values, id);
15222             record.json = n;
15223             records[records.length] = record;
15224         }
15225         return {
15226             records : records,
15227             totalRecords : records.length
15228         };
15229     },
15230     // used when loading children.. @see loadDataFromChildren
15231     toLoadData: function(rec)
15232     {
15233         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15234         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15235         
15236     }
15237     
15238     
15239 });/*
15240  * - LGPL
15241  * * 
15242  */
15243
15244 /**
15245  * @class Roo.bootstrap.ComboBox
15246  * @extends Roo.bootstrap.TriggerField
15247  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15248  * @cfg {Boolean} append (true|false) default false
15249  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15250  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15251  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15252  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15253  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15254  * @cfg {Boolean} animate default true
15255  * @cfg {Boolean} emptyResultText only for touch device
15256  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15257  * @cfg {String} emptyTitle default ''
15258  * @cfg {Number} width fixed with? experimental
15259  * @constructor
15260  * Create a new ComboBox.
15261  * @param {Object} config Configuration options
15262  */
15263 Roo.bootstrap.ComboBox = function(config){
15264     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15265     this.addEvents({
15266         /**
15267          * @event expand
15268          * Fires when the dropdown list is expanded
15269         * @param {Roo.bootstrap.ComboBox} combo This combo box
15270         */
15271         'expand' : true,
15272         /**
15273          * @event collapse
15274          * Fires when the dropdown list is collapsed
15275         * @param {Roo.bootstrap.ComboBox} combo This combo box
15276         */
15277         'collapse' : true,
15278         /**
15279          * @event beforeselect
15280          * Fires before a list item is selected. Return false to cancel the selection.
15281         * @param {Roo.bootstrap.ComboBox} combo This combo box
15282         * @param {Roo.data.Record} record The data record returned from the underlying store
15283         * @param {Number} index The index of the selected item in the dropdown list
15284         */
15285         'beforeselect' : true,
15286         /**
15287          * @event select
15288          * Fires when a list item is selected
15289         * @param {Roo.bootstrap.ComboBox} combo This combo box
15290         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15291         * @param {Number} index The index of the selected item in the dropdown list
15292         */
15293         'select' : true,
15294         /**
15295          * @event beforequery
15296          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15297          * The event object passed has these properties:
15298         * @param {Roo.bootstrap.ComboBox} combo This combo box
15299         * @param {String} query The query
15300         * @param {Boolean} forceAll true to force "all" query
15301         * @param {Boolean} cancel true to cancel the query
15302         * @param {Object} e The query event object
15303         */
15304         'beforequery': true,
15305          /**
15306          * @event add
15307          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15308         * @param {Roo.bootstrap.ComboBox} combo This combo box
15309         */
15310         'add' : true,
15311         /**
15312          * @event edit
15313          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15314         * @param {Roo.bootstrap.ComboBox} combo This combo box
15315         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15316         */
15317         'edit' : true,
15318         /**
15319          * @event remove
15320          * Fires when the remove value from the combobox array
15321         * @param {Roo.bootstrap.ComboBox} combo This combo box
15322         */
15323         'remove' : true,
15324         /**
15325          * @event afterremove
15326          * Fires when the remove value from the combobox array
15327         * @param {Roo.bootstrap.ComboBox} combo This combo box
15328         */
15329         'afterremove' : true,
15330         /**
15331          * @event specialfilter
15332          * Fires when specialfilter
15333             * @param {Roo.bootstrap.ComboBox} combo This combo box
15334             */
15335         'specialfilter' : true,
15336         /**
15337          * @event tick
15338          * Fires when tick the element
15339             * @param {Roo.bootstrap.ComboBox} combo This combo box
15340             */
15341         'tick' : true,
15342         /**
15343          * @event touchviewdisplay
15344          * Fires when touch view require special display (default is using displayField)
15345             * @param {Roo.bootstrap.ComboBox} combo This combo box
15346             * @param {Object} cfg set html .
15347             */
15348         'touchviewdisplay' : true
15349         
15350     });
15351     
15352     this.item = [];
15353     this.tickItems = [];
15354     
15355     this.selectedIndex = -1;
15356     if(this.mode == 'local'){
15357         if(config.queryDelay === undefined){
15358             this.queryDelay = 10;
15359         }
15360         if(config.minChars === undefined){
15361             this.minChars = 0;
15362         }
15363     }
15364 };
15365
15366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15367      
15368     /**
15369      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15370      * rendering into an Roo.Editor, defaults to false)
15371      */
15372     /**
15373      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15374      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15375      */
15376     /**
15377      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15378      */
15379     /**
15380      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15381      * the dropdown list (defaults to undefined, with no header element)
15382      */
15383
15384      /**
15385      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15386      */
15387      
15388      /**
15389      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15390      */
15391     listWidth: undefined,
15392     /**
15393      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15394      * mode = 'remote' or 'text' if mode = 'local')
15395      */
15396     displayField: undefined,
15397     
15398     /**
15399      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15400      * mode = 'remote' or 'value' if mode = 'local'). 
15401      * Note: use of a valueField requires the user make a selection
15402      * in order for a value to be mapped.
15403      */
15404     valueField: undefined,
15405     /**
15406      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15407      */
15408     modalTitle : '',
15409     
15410     /**
15411      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15412      * field's data value (defaults to the underlying DOM element's name)
15413      */
15414     hiddenName: undefined,
15415     /**
15416      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15417      */
15418     listClass: '',
15419     /**
15420      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15421      */
15422     selectedClass: 'active',
15423     
15424     /**
15425      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15426      */
15427     shadow:'sides',
15428     /**
15429      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15430      * anchor positions (defaults to 'tl-bl')
15431      */
15432     listAlign: 'tl-bl?',
15433     /**
15434      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15435      */
15436     maxHeight: 300,
15437     /**
15438      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15439      * query specified by the allQuery config option (defaults to 'query')
15440      */
15441     triggerAction: 'query',
15442     /**
15443      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15444      * (defaults to 4, does not apply if editable = false)
15445      */
15446     minChars : 4,
15447     /**
15448      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15449      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15450      */
15451     typeAhead: false,
15452     /**
15453      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15454      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15455      */
15456     queryDelay: 500,
15457     /**
15458      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15459      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15460      */
15461     pageSize: 0,
15462     /**
15463      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15464      * when editable = true (defaults to false)
15465      */
15466     selectOnFocus:false,
15467     /**
15468      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15469      */
15470     queryParam: 'query',
15471     /**
15472      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15473      * when mode = 'remote' (defaults to 'Loading...')
15474      */
15475     loadingText: 'Loading...',
15476     /**
15477      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15478      */
15479     resizable: false,
15480     /**
15481      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15482      */
15483     handleHeight : 8,
15484     /**
15485      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15486      * traditional select (defaults to true)
15487      */
15488     editable: true,
15489     /**
15490      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15491      */
15492     allQuery: '',
15493     /**
15494      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15495      */
15496     mode: 'remote',
15497     /**
15498      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15499      * listWidth has a higher value)
15500      */
15501     minListWidth : 70,
15502     /**
15503      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15504      * allow the user to set arbitrary text into the field (defaults to false)
15505      */
15506     forceSelection:false,
15507     /**
15508      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15509      * if typeAhead = true (defaults to 250)
15510      */
15511     typeAheadDelay : 250,
15512     /**
15513      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15514      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15515      */
15516     valueNotFoundText : undefined,
15517     /**
15518      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15519      */
15520     blockFocus : false,
15521     
15522     /**
15523      * @cfg {Boolean} disableClear Disable showing of clear button.
15524      */
15525     disableClear : false,
15526     /**
15527      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15528      */
15529     alwaysQuery : false,
15530     
15531     /**
15532      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15533      */
15534     multiple : false,
15535     
15536     /**
15537      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15538      */
15539     invalidClass : "has-warning",
15540     
15541     /**
15542      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15543      */
15544     validClass : "has-success",
15545     
15546     /**
15547      * @cfg {Boolean} specialFilter (true|false) special filter default false
15548      */
15549     specialFilter : false,
15550     
15551     /**
15552      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15553      */
15554     mobileTouchView : true,
15555     
15556     /**
15557      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15558      */
15559     useNativeIOS : false,
15560     
15561     /**
15562      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15563      */
15564     mobile_restrict_height : false,
15565     
15566     ios_options : false,
15567     
15568     //private
15569     addicon : false,
15570     editicon: false,
15571     
15572     page: 0,
15573     hasQuery: false,
15574     append: false,
15575     loadNext: false,
15576     autoFocus : true,
15577     tickable : false,
15578     btnPosition : 'right',
15579     triggerList : true,
15580     showToggleBtn : true,
15581     animate : true,
15582     emptyResultText: 'Empty',
15583     triggerText : 'Select',
15584     emptyTitle : '',
15585     width : false,
15586     
15587     // element that contains real text value.. (when hidden is used..)
15588     
15589     getAutoCreate : function()
15590     {   
15591         var cfg = false;
15592         //render
15593         /*
15594          * Render classic select for iso
15595          */
15596         
15597         if(Roo.isIOS && this.useNativeIOS){
15598             cfg = this.getAutoCreateNativeIOS();
15599             return cfg;
15600         }
15601         
15602         /*
15603          * Touch Devices
15604          */
15605         
15606         if(Roo.isTouch && this.mobileTouchView){
15607             cfg = this.getAutoCreateTouchView();
15608             return cfg;;
15609         }
15610         
15611         /*
15612          *  Normal ComboBox
15613          */
15614         if(!this.tickable){
15615             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15616             return cfg;
15617         }
15618         
15619         /*
15620          *  ComboBox with tickable selections
15621          */
15622              
15623         var align = this.labelAlign || this.parentLabelAlign();
15624         
15625         cfg = {
15626             cls : 'form-group roo-combobox-tickable' //input-group
15627         };
15628         
15629         var btn_text_select = '';
15630         var btn_text_done = '';
15631         var btn_text_cancel = '';
15632         
15633         if (this.btn_text_show) {
15634             btn_text_select = 'Select';
15635             btn_text_done = 'Done';
15636             btn_text_cancel = 'Cancel'; 
15637         }
15638         
15639         var buttons = {
15640             tag : 'div',
15641             cls : 'tickable-buttons',
15642             cn : [
15643                 {
15644                     tag : 'button',
15645                     type : 'button',
15646                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15647                     //html : this.triggerText
15648                     html: btn_text_select
15649                 },
15650                 {
15651                     tag : 'button',
15652                     type : 'button',
15653                     name : 'ok',
15654                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15655                     //html : 'Done'
15656                     html: btn_text_done
15657                 },
15658                 {
15659                     tag : 'button',
15660                     type : 'button',
15661                     name : 'cancel',
15662                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15663                     //html : 'Cancel'
15664                     html: btn_text_cancel
15665                 }
15666             ]
15667         };
15668         
15669         if(this.editable){
15670             buttons.cn.unshift({
15671                 tag: 'input',
15672                 cls: 'roo-select2-search-field-input'
15673             });
15674         }
15675         
15676         var _this = this;
15677         
15678         Roo.each(buttons.cn, function(c){
15679             if (_this.size) {
15680                 c.cls += ' btn-' + _this.size;
15681             }
15682
15683             if (_this.disabled) {
15684                 c.disabled = true;
15685             }
15686         });
15687         
15688         var box = {
15689             tag: 'div',
15690             style : 'display: contents',
15691             cn: [
15692                 {
15693                     tag: 'input',
15694                     type : 'hidden',
15695                     cls: 'form-hidden-field'
15696                 },
15697                 {
15698                     tag: 'ul',
15699                     cls: 'roo-select2-choices',
15700                     cn:[
15701                         {
15702                             tag: 'li',
15703                             cls: 'roo-select2-search-field',
15704                             cn: [
15705                                 buttons
15706                             ]
15707                         }
15708                     ]
15709                 }
15710             ]
15711         };
15712         
15713         var combobox = {
15714             cls: 'roo-select2-container input-group roo-select2-container-multi',
15715             cn: [
15716                 
15717                 box
15718 //                {
15719 //                    tag: 'ul',
15720 //                    cls: 'typeahead typeahead-long dropdown-menu',
15721 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15722 //                }
15723             ]
15724         };
15725         
15726         if(this.hasFeedback && !this.allowBlank){
15727             
15728             var feedback = {
15729                 tag: 'span',
15730                 cls: 'glyphicon form-control-feedback'
15731             };
15732
15733             combobox.cn.push(feedback);
15734         }
15735         
15736         
15737         
15738         var indicator = {
15739             tag : 'i',
15740             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15741             tooltip : 'This field is required'
15742         };
15743         if (Roo.bootstrap.version == 4) {
15744             indicator = {
15745                 tag : 'i',
15746                 style : 'display:none'
15747             };
15748         }
15749         if (align ==='left' && this.fieldLabel.length) {
15750             
15751             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15752             
15753             cfg.cn = [
15754                 indicator,
15755                 {
15756                     tag: 'label',
15757                     'for' :  id,
15758                     cls : 'control-label col-form-label',
15759                     html : this.fieldLabel
15760
15761                 },
15762                 {
15763                     cls : "", 
15764                     cn: [
15765                         combobox
15766                     ]
15767                 }
15768
15769             ];
15770             
15771             var labelCfg = cfg.cn[1];
15772             var contentCfg = cfg.cn[2];
15773             
15774
15775             if(this.indicatorpos == 'right'){
15776                 
15777                 cfg.cn = [
15778                     {
15779                         tag: 'label',
15780                         'for' :  id,
15781                         cls : 'control-label col-form-label',
15782                         cn : [
15783                             {
15784                                 tag : 'span',
15785                                 html : this.fieldLabel
15786                             },
15787                             indicator
15788                         ]
15789                     },
15790                     {
15791                         cls : "",
15792                         cn: [
15793                             combobox
15794                         ]
15795                     }
15796
15797                 ];
15798                 
15799                 
15800                 
15801                 labelCfg = cfg.cn[0];
15802                 contentCfg = cfg.cn[1];
15803             
15804             }
15805             
15806             if(this.labelWidth > 12){
15807                 labelCfg.style = "width: " + this.labelWidth + 'px';
15808             }
15809             if(this.width * 1 > 0){
15810                 contentCfg.style = "width: " + this.width + 'px';
15811             }
15812             if(this.labelWidth < 13 && this.labelmd == 0){
15813                 this.labelmd = this.labelWidth;
15814             }
15815             
15816             if(this.labellg > 0){
15817                 labelCfg.cls += ' col-lg-' + this.labellg;
15818                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15819             }
15820             
15821             if(this.labelmd > 0){
15822                 labelCfg.cls += ' col-md-' + this.labelmd;
15823                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15824             }
15825             
15826             if(this.labelsm > 0){
15827                 labelCfg.cls += ' col-sm-' + this.labelsm;
15828                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15829             }
15830             
15831             if(this.labelxs > 0){
15832                 labelCfg.cls += ' col-xs-' + this.labelxs;
15833                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15834             }
15835                 
15836                 
15837         } else if ( this.fieldLabel.length) {
15838 //                Roo.log(" label");
15839                  cfg.cn = [
15840                    indicator,
15841                     {
15842                         tag: 'label',
15843                         //cls : 'input-group-addon',
15844                         html : this.fieldLabel
15845                     },
15846                     combobox
15847                 ];
15848                 
15849                 if(this.indicatorpos == 'right'){
15850                     cfg.cn = [
15851                         {
15852                             tag: 'label',
15853                             //cls : 'input-group-addon',
15854                             html : this.fieldLabel
15855                         },
15856                         indicator,
15857                         combobox
15858                     ];
15859                     
15860                 }
15861
15862         } else {
15863             
15864 //                Roo.log(" no label && no align");
15865                 cfg = combobox
15866                      
15867                 
15868         }
15869          
15870         var settings=this;
15871         ['xs','sm','md','lg'].map(function(size){
15872             if (settings[size]) {
15873                 cfg.cls += ' col-' + size + '-' + settings[size];
15874             }
15875         });
15876         
15877         return cfg;
15878         
15879     },
15880     
15881     _initEventsCalled : false,
15882     
15883     // private
15884     initEvents: function()
15885     {   
15886         if (this._initEventsCalled) { // as we call render... prevent looping...
15887             return;
15888         }
15889         this._initEventsCalled = true;
15890         
15891         if (!this.store) {
15892             throw "can not find store for combo";
15893         }
15894         
15895         this.indicator = this.indicatorEl();
15896         
15897         this.store = Roo.factory(this.store, Roo.data);
15898         this.store.parent = this;
15899         
15900         // if we are building from html. then this element is so complex, that we can not really
15901         // use the rendered HTML.
15902         // so we have to trash and replace the previous code.
15903         if (Roo.XComponent.build_from_html) {
15904             // remove this element....
15905             var e = this.el.dom, k=0;
15906             while (e ) { e = e.previousSibling;  ++k;}
15907
15908             this.el.remove();
15909             
15910             this.el=false;
15911             this.rendered = false;
15912             
15913             this.render(this.parent().getChildContainer(true), k);
15914         }
15915         
15916         if(Roo.isIOS && this.useNativeIOS){
15917             this.initIOSView();
15918             return;
15919         }
15920         
15921         /*
15922          * Touch Devices
15923          */
15924         
15925         if(Roo.isTouch && this.mobileTouchView){
15926             this.initTouchView();
15927             return;
15928         }
15929         
15930         if(this.tickable){
15931             this.initTickableEvents();
15932             return;
15933         }
15934         
15935         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15936         
15937         if(this.hiddenName){
15938             
15939             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15940             
15941             this.hiddenField.dom.value =
15942                 this.hiddenValue !== undefined ? this.hiddenValue :
15943                 this.value !== undefined ? this.value : '';
15944
15945             // prevent input submission
15946             this.el.dom.removeAttribute('name');
15947             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15948              
15949              
15950         }
15951         //if(Roo.isGecko){
15952         //    this.el.dom.setAttribute('autocomplete', 'off');
15953         //}
15954         
15955         var cls = 'x-combo-list';
15956         
15957         //this.list = new Roo.Layer({
15958         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15959         //});
15960         
15961         var _this = this;
15962         
15963         (function(){
15964             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15965             _this.list.setWidth(lw);
15966         }).defer(100);
15967         
15968         this.list.on('mouseover', this.onViewOver, this);
15969         this.list.on('mousemove', this.onViewMove, this);
15970         this.list.on('scroll', this.onViewScroll, this);
15971         
15972         /*
15973         this.list.swallowEvent('mousewheel');
15974         this.assetHeight = 0;
15975
15976         if(this.title){
15977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15978             this.assetHeight += this.header.getHeight();
15979         }
15980
15981         this.innerList = this.list.createChild({cls:cls+'-inner'});
15982         this.innerList.on('mouseover', this.onViewOver, this);
15983         this.innerList.on('mousemove', this.onViewMove, this);
15984         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15985         
15986         if(this.allowBlank && !this.pageSize && !this.disableClear){
15987             this.footer = this.list.createChild({cls:cls+'-ft'});
15988             this.pageTb = new Roo.Toolbar(this.footer);
15989            
15990         }
15991         if(this.pageSize){
15992             this.footer = this.list.createChild({cls:cls+'-ft'});
15993             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15994                     {pageSize: this.pageSize});
15995             
15996         }
15997         
15998         if (this.pageTb && this.allowBlank && !this.disableClear) {
15999             var _this = this;
16000             this.pageTb.add(new Roo.Toolbar.Fill(), {
16001                 cls: 'x-btn-icon x-btn-clear',
16002                 text: '&#160;',
16003                 handler: function()
16004                 {
16005                     _this.collapse();
16006                     _this.clearValue();
16007                     _this.onSelect(false, -1);
16008                 }
16009             });
16010         }
16011         if (this.footer) {
16012             this.assetHeight += this.footer.getHeight();
16013         }
16014         */
16015             
16016         if(!this.tpl){
16017             this.tpl = Roo.bootstrap.version == 4 ?
16018                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16019                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16020         }
16021
16022         this.view = new Roo.View(this.list, this.tpl, {
16023             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16024         });
16025         //this.view.wrapEl.setDisplayed(false);
16026         this.view.on('click', this.onViewClick, this);
16027         
16028         
16029         this.store.on('beforeload', this.onBeforeLoad, this);
16030         this.store.on('load', this.onLoad, this);
16031         this.store.on('loadexception', this.onLoadException, this);
16032         /*
16033         if(this.resizable){
16034             this.resizer = new Roo.Resizable(this.list,  {
16035                pinned:true, handles:'se'
16036             });
16037             this.resizer.on('resize', function(r, w, h){
16038                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16039                 this.listWidth = w;
16040                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16041                 this.restrictHeight();
16042             }, this);
16043             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16044         }
16045         */
16046         if(!this.editable){
16047             this.editable = true;
16048             this.setEditable(false);
16049         }
16050         
16051         /*
16052         
16053         if (typeof(this.events.add.listeners) != 'undefined') {
16054             
16055             this.addicon = this.wrap.createChild(
16056                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16057        
16058             this.addicon.on('click', function(e) {
16059                 this.fireEvent('add', this);
16060             }, this);
16061         }
16062         if (typeof(this.events.edit.listeners) != 'undefined') {
16063             
16064             this.editicon = this.wrap.createChild(
16065                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16066             if (this.addicon) {
16067                 this.editicon.setStyle('margin-left', '40px');
16068             }
16069             this.editicon.on('click', function(e) {
16070                 
16071                 // we fire even  if inothing is selected..
16072                 this.fireEvent('edit', this, this.lastData );
16073                 
16074             }, this);
16075         }
16076         */
16077         
16078         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16079             "up" : function(e){
16080                 this.inKeyMode = true;
16081                 this.selectPrev();
16082             },
16083
16084             "down" : function(e){
16085                 if(!this.isExpanded()){
16086                     this.onTriggerClick();
16087                 }else{
16088                     this.inKeyMode = true;
16089                     this.selectNext();
16090                 }
16091             },
16092
16093             "enter" : function(e){
16094 //                this.onViewClick();
16095                 //return true;
16096                 this.collapse();
16097                 
16098                 if(this.fireEvent("specialkey", this, e)){
16099                     this.onViewClick(false);
16100                 }
16101                 
16102                 return true;
16103             },
16104
16105             "esc" : function(e){
16106                 this.collapse();
16107             },
16108
16109             "tab" : function(e){
16110                 this.collapse();
16111                 
16112                 if(this.fireEvent("specialkey", this, e)){
16113                     this.onViewClick(false);
16114                 }
16115                 
16116                 return true;
16117             },
16118
16119             scope : this,
16120
16121             doRelay : function(foo, bar, hname){
16122                 if(hname == 'down' || this.scope.isExpanded()){
16123                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16124                 }
16125                 return true;
16126             },
16127
16128             forceKeyDown: true
16129         });
16130         
16131         
16132         this.queryDelay = Math.max(this.queryDelay || 10,
16133                 this.mode == 'local' ? 10 : 250);
16134         
16135         
16136         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16137         
16138         if(this.typeAhead){
16139             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16140         }
16141         if(this.editable !== false){
16142             this.inputEl().on("keyup", this.onKeyUp, this);
16143         }
16144         if(this.forceSelection){
16145             this.inputEl().on('blur', this.doForce, this);
16146         }
16147         
16148         if(this.multiple){
16149             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16150             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16151         }
16152     },
16153     
16154     initTickableEvents: function()
16155     {   
16156         this.createList();
16157         
16158         if(this.hiddenName){
16159             
16160             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16161             
16162             this.hiddenField.dom.value =
16163                 this.hiddenValue !== undefined ? this.hiddenValue :
16164                 this.value !== undefined ? this.value : '';
16165
16166             // prevent input submission
16167             this.el.dom.removeAttribute('name');
16168             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16169              
16170              
16171         }
16172         
16173 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16174         
16175         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16176         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16177         if(this.triggerList){
16178             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16179         }
16180          
16181         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16182         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16183         
16184         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16185         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16186         
16187         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16188         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16189         
16190         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16191         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16192         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16193         
16194         this.okBtn.hide();
16195         this.cancelBtn.hide();
16196         
16197         var _this = this;
16198         
16199         (function(){
16200             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16201             _this.list.setWidth(lw);
16202         }).defer(100);
16203         
16204         this.list.on('mouseover', this.onViewOver, this);
16205         this.list.on('mousemove', this.onViewMove, this);
16206         
16207         this.list.on('scroll', this.onViewScroll, this);
16208         
16209         if(!this.tpl){
16210             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16211                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16212         }
16213
16214         this.view = new Roo.View(this.list, this.tpl, {
16215             singleSelect:true,
16216             tickable:true,
16217             parent:this,
16218             store: this.store,
16219             selectedClass: this.selectedClass
16220         });
16221         
16222         //this.view.wrapEl.setDisplayed(false);
16223         this.view.on('click', this.onViewClick, this);
16224         
16225         
16226         
16227         this.store.on('beforeload', this.onBeforeLoad, this);
16228         this.store.on('load', this.onLoad, this);
16229         this.store.on('loadexception', this.onLoadException, this);
16230         
16231         if(this.editable){
16232             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16233                 "up" : function(e){
16234                     this.inKeyMode = true;
16235                     this.selectPrev();
16236                 },
16237
16238                 "down" : function(e){
16239                     this.inKeyMode = true;
16240                     this.selectNext();
16241                 },
16242
16243                 "enter" : function(e){
16244                     if(this.fireEvent("specialkey", this, e)){
16245                         this.onViewClick(false);
16246                     }
16247                     
16248                     return true;
16249                 },
16250
16251                 "esc" : function(e){
16252                     this.onTickableFooterButtonClick(e, false, false);
16253                 },
16254
16255                 "tab" : function(e){
16256                     this.fireEvent("specialkey", this, e);
16257                     
16258                     this.onTickableFooterButtonClick(e, false, false);
16259                     
16260                     return true;
16261                 },
16262
16263                 scope : this,
16264
16265                 doRelay : function(e, fn, key){
16266                     if(this.scope.isExpanded()){
16267                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16268                     }
16269                     return true;
16270                 },
16271
16272                 forceKeyDown: true
16273             });
16274         }
16275         
16276         this.queryDelay = Math.max(this.queryDelay || 10,
16277                 this.mode == 'local' ? 10 : 250);
16278         
16279         
16280         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16281         
16282         if(this.typeAhead){
16283             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16284         }
16285         
16286         if(this.editable !== false){
16287             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16288         }
16289         
16290         this.indicator = this.indicatorEl();
16291         
16292         if(this.indicator){
16293             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16294             this.indicator.hide();
16295         }
16296         
16297     },
16298
16299     onDestroy : function(){
16300         if(this.view){
16301             this.view.setStore(null);
16302             this.view.el.removeAllListeners();
16303             this.view.el.remove();
16304             this.view.purgeListeners();
16305         }
16306         if(this.list){
16307             this.list.dom.innerHTML  = '';
16308         }
16309         
16310         if(this.store){
16311             this.store.un('beforeload', this.onBeforeLoad, this);
16312             this.store.un('load', this.onLoad, this);
16313             this.store.un('loadexception', this.onLoadException, this);
16314         }
16315         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16316     },
16317
16318     // private
16319     fireKey : function(e){
16320         if(e.isNavKeyPress() && !this.list.isVisible()){
16321             this.fireEvent("specialkey", this, e);
16322         }
16323     },
16324
16325     // private
16326     onResize: function(w, h)
16327     {
16328         
16329         
16330 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16331 //        
16332 //        if(typeof w != 'number'){
16333 //            // we do not handle it!?!?
16334 //            return;
16335 //        }
16336 //        var tw = this.trigger.getWidth();
16337 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16338 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16339 //        var x = w - tw;
16340 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16341 //            
16342 //        //this.trigger.setStyle('left', x+'px');
16343 //        
16344 //        if(this.list && this.listWidth === undefined){
16345 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16346 //            this.list.setWidth(lw);
16347 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16348 //        }
16349         
16350     
16351         
16352     },
16353
16354     /**
16355      * Allow or prevent the user from directly editing the field text.  If false is passed,
16356      * the user will only be able to select from the items defined in the dropdown list.  This method
16357      * is the runtime equivalent of setting the 'editable' config option at config time.
16358      * @param {Boolean} value True to allow the user to directly edit the field text
16359      */
16360     setEditable : function(value){
16361         if(value == this.editable){
16362             return;
16363         }
16364         this.editable = value;
16365         if(!value){
16366             this.inputEl().dom.setAttribute('readOnly', true);
16367             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16368             this.inputEl().addClass('x-combo-noedit');
16369         }else{
16370             this.inputEl().dom.setAttribute('readOnly', false);
16371             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16372             this.inputEl().removeClass('x-combo-noedit');
16373         }
16374     },
16375
16376     // private
16377     
16378     onBeforeLoad : function(combo,opts){
16379         if(!this.hasFocus){
16380             return;
16381         }
16382          if (!opts.add) {
16383             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16384          }
16385         this.restrictHeight();
16386         this.selectedIndex = -1;
16387     },
16388
16389     // private
16390     onLoad : function(){
16391         
16392         this.hasQuery = false;
16393         
16394         if(!this.hasFocus){
16395             return;
16396         }
16397         
16398         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16399             this.loading.hide();
16400         }
16401         
16402         if(this.store.getCount() > 0){
16403             
16404             this.expand();
16405             this.restrictHeight();
16406             if(this.lastQuery == this.allQuery){
16407                 if(this.editable && !this.tickable){
16408                     this.inputEl().dom.select();
16409                 }
16410                 
16411                 if(
16412                     !this.selectByValue(this.value, true) &&
16413                     this.autoFocus && 
16414                     (
16415                         !this.store.lastOptions ||
16416                         typeof(this.store.lastOptions.add) == 'undefined' || 
16417                         this.store.lastOptions.add != true
16418                     )
16419                 ){
16420                     this.select(0, true);
16421                 }
16422             }else{
16423                 if(this.autoFocus){
16424                     this.selectNext();
16425                 }
16426                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16427                     this.taTask.delay(this.typeAheadDelay);
16428                 }
16429             }
16430         }else{
16431             this.onEmptyResults();
16432         }
16433         
16434         //this.el.focus();
16435     },
16436     // private
16437     onLoadException : function()
16438     {
16439         this.hasQuery = false;
16440         
16441         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442             this.loading.hide();
16443         }
16444         
16445         if(this.tickable && this.editable){
16446             return;
16447         }
16448         
16449         this.collapse();
16450         // only causes errors at present
16451         //Roo.log(this.store.reader.jsonData);
16452         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16453             // fixme
16454             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16455         //}
16456         
16457         
16458     },
16459     // private
16460     onTypeAhead : function(){
16461         if(this.store.getCount() > 0){
16462             var r = this.store.getAt(0);
16463             var newValue = r.data[this.displayField];
16464             var len = newValue.length;
16465             var selStart = this.getRawValue().length;
16466             
16467             if(selStart != len){
16468                 this.setRawValue(newValue);
16469                 this.selectText(selStart, newValue.length);
16470             }
16471         }
16472     },
16473
16474     // private
16475     onSelect : function(record, index){
16476         
16477         if(this.fireEvent('beforeselect', this, record, index) !== false){
16478         
16479             this.setFromData(index > -1 ? record.data : false);
16480             
16481             this.collapse();
16482             this.fireEvent('select', this, record, index);
16483         }
16484     },
16485
16486     /**
16487      * Returns the currently selected field value or empty string if no value is set.
16488      * @return {String} value The selected value
16489      */
16490     getValue : function()
16491     {
16492         if(Roo.isIOS && this.useNativeIOS){
16493             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16494         }
16495         
16496         if(this.multiple){
16497             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16498         }
16499         
16500         if(this.valueField){
16501             return typeof this.value != 'undefined' ? this.value : '';
16502         }else{
16503             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16504         }
16505     },
16506     
16507     getRawValue : function()
16508     {
16509         if(Roo.isIOS && this.useNativeIOS){
16510             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16511         }
16512         
16513         var v = this.inputEl().getValue();
16514         
16515         return v;
16516     },
16517
16518     /**
16519      * Clears any text/value currently set in the field
16520      */
16521     clearValue : function(){
16522         
16523         if(this.hiddenField){
16524             this.hiddenField.dom.value = '';
16525         }
16526         this.value = '';
16527         this.setRawValue('');
16528         this.lastSelectionText = '';
16529         this.lastData = false;
16530         
16531         var close = this.closeTriggerEl();
16532         
16533         if(close){
16534             close.hide();
16535         }
16536         
16537         this.validate();
16538         
16539     },
16540
16541     /**
16542      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16543      * will be displayed in the field.  If the value does not match the data value of an existing item,
16544      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16545      * Otherwise the field will be blank (although the value will still be set).
16546      * @param {String} value The value to match
16547      */
16548     setValue : function(v)
16549     {
16550         if(Roo.isIOS && this.useNativeIOS){
16551             this.setIOSValue(v);
16552             return;
16553         }
16554         
16555         if(this.multiple){
16556             this.syncValue();
16557             return;
16558         }
16559         
16560         var text = v;
16561         if(this.valueField){
16562             var r = this.findRecord(this.valueField, v);
16563             if(r){
16564                 text = r.data[this.displayField];
16565             }else if(this.valueNotFoundText !== undefined){
16566                 text = this.valueNotFoundText;
16567             }
16568         }
16569         this.lastSelectionText = text;
16570         if(this.hiddenField){
16571             this.hiddenField.dom.value = v;
16572         }
16573         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16574         this.value = v;
16575         
16576         var close = this.closeTriggerEl();
16577         
16578         if(close){
16579             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16580         }
16581         
16582         this.validate();
16583     },
16584     /**
16585      * @property {Object} the last set data for the element
16586      */
16587     
16588     lastData : false,
16589     /**
16590      * Sets the value of the field based on a object which is related to the record format for the store.
16591      * @param {Object} value the value to set as. or false on reset?
16592      */
16593     setFromData : function(o){
16594         
16595         if(this.multiple){
16596             this.addItem(o);
16597             return;
16598         }
16599             
16600         var dv = ''; // display value
16601         var vv = ''; // value value..
16602         this.lastData = o;
16603         if (this.displayField) {
16604             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16605         } else {
16606             // this is an error condition!!!
16607             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16608         }
16609         
16610         if(this.valueField){
16611             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16612         }
16613         
16614         var close = this.closeTriggerEl();
16615         
16616         if(close){
16617             if(dv.length || vv * 1 > 0){
16618                 close.show() ;
16619                 this.blockFocus=true;
16620             } else {
16621                 close.hide();
16622             }             
16623         }
16624         
16625         if(this.hiddenField){
16626             this.hiddenField.dom.value = vv;
16627             
16628             this.lastSelectionText = dv;
16629             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16630             this.value = vv;
16631             return;
16632         }
16633         // no hidden field.. - we store the value in 'value', but still display
16634         // display field!!!!
16635         this.lastSelectionText = dv;
16636         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16637         this.value = vv;
16638         
16639         
16640         
16641     },
16642     // private
16643     reset : function(){
16644         // overridden so that last data is reset..
16645         
16646         if(this.multiple){
16647             this.clearItem();
16648             return;
16649         }
16650         
16651         this.setValue(this.originalValue);
16652         //this.clearInvalid();
16653         this.lastData = false;
16654         if (this.view) {
16655             this.view.clearSelections();
16656         }
16657         
16658         this.validate();
16659     },
16660     // private
16661     findRecord : function(prop, value){
16662         var record;
16663         if(this.store.getCount() > 0){
16664             this.store.each(function(r){
16665                 if(r.data[prop] == value){
16666                     record = r;
16667                     return false;
16668                 }
16669                 return true;
16670             });
16671         }
16672         return record;
16673     },
16674     
16675     getName: function()
16676     {
16677         // returns hidden if it's set..
16678         if (!this.rendered) {return ''};
16679         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16680         
16681     },
16682     // private
16683     onViewMove : function(e, t){
16684         this.inKeyMode = false;
16685     },
16686
16687     // private
16688     onViewOver : function(e, t){
16689         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16690             return;
16691         }
16692         var item = this.view.findItemFromChild(t);
16693         
16694         if(item){
16695             var index = this.view.indexOf(item);
16696             this.select(index, false);
16697         }
16698     },
16699
16700     // private
16701     onViewClick : function(view, doFocus, el, e)
16702     {
16703         var index = this.view.getSelectedIndexes()[0];
16704         
16705         var r = this.store.getAt(index);
16706         
16707         if(this.tickable){
16708             
16709             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16710                 return;
16711             }
16712             
16713             var rm = false;
16714             var _this = this;
16715             
16716             Roo.each(this.tickItems, function(v,k){
16717                 
16718                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16719                     Roo.log(v);
16720                     _this.tickItems.splice(k, 1);
16721                     
16722                     if(typeof(e) == 'undefined' && view == false){
16723                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16724                     }
16725                     
16726                     rm = true;
16727                     return;
16728                 }
16729             });
16730             
16731             if(rm){
16732                 return;
16733             }
16734             
16735             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16736                 this.tickItems.push(r.data);
16737             }
16738             
16739             if(typeof(e) == 'undefined' && view == false){
16740                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16741             }
16742                     
16743             return;
16744         }
16745         
16746         if(r){
16747             this.onSelect(r, index);
16748         }
16749         if(doFocus !== false && !this.blockFocus){
16750             this.inputEl().focus();
16751         }
16752     },
16753
16754     // private
16755     restrictHeight : function(){
16756         //this.innerList.dom.style.height = '';
16757         //var inner = this.innerList.dom;
16758         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16759         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16760         //this.list.beginUpdate();
16761         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16762         this.list.alignTo(this.inputEl(), this.listAlign);
16763         this.list.alignTo(this.inputEl(), this.listAlign);
16764         //this.list.endUpdate();
16765     },
16766
16767     // private
16768     onEmptyResults : function(){
16769         
16770         if(this.tickable && this.editable){
16771             this.hasFocus = false;
16772             this.restrictHeight();
16773             return;
16774         }
16775         
16776         this.collapse();
16777     },
16778
16779     /**
16780      * Returns true if the dropdown list is expanded, else false.
16781      */
16782     isExpanded : function(){
16783         return this.list.isVisible();
16784     },
16785
16786     /**
16787      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16788      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16789      * @param {String} value The data value of the item to select
16790      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16791      * selected item if it is not currently in view (defaults to true)
16792      * @return {Boolean} True if the value matched an item in the list, else false
16793      */
16794     selectByValue : function(v, scrollIntoView){
16795         if(v !== undefined && v !== null){
16796             var r = this.findRecord(this.valueField || this.displayField, v);
16797             if(r){
16798                 this.select(this.store.indexOf(r), scrollIntoView);
16799                 return true;
16800             }
16801         }
16802         return false;
16803     },
16804
16805     /**
16806      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16807      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16808      * @param {Number} index The zero-based index of the list item to select
16809      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16810      * selected item if it is not currently in view (defaults to true)
16811      */
16812     select : function(index, scrollIntoView){
16813         this.selectedIndex = index;
16814         this.view.select(index);
16815         if(scrollIntoView !== false){
16816             var el = this.view.getNode(index);
16817             /*
16818              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16819              */
16820             if(el){
16821                 this.list.scrollChildIntoView(el, false);
16822             }
16823         }
16824     },
16825
16826     // private
16827     selectNext : function(){
16828         var ct = this.store.getCount();
16829         if(ct > 0){
16830             if(this.selectedIndex == -1){
16831                 this.select(0);
16832             }else if(this.selectedIndex < ct-1){
16833                 this.select(this.selectedIndex+1);
16834             }
16835         }
16836     },
16837
16838     // private
16839     selectPrev : function(){
16840         var ct = this.store.getCount();
16841         if(ct > 0){
16842             if(this.selectedIndex == -1){
16843                 this.select(0);
16844             }else if(this.selectedIndex != 0){
16845                 this.select(this.selectedIndex-1);
16846             }
16847         }
16848     },
16849
16850     // private
16851     onKeyUp : function(e){
16852         if(this.editable !== false && !e.isSpecialKey()){
16853             this.lastKey = e.getKey();
16854             this.dqTask.delay(this.queryDelay);
16855         }
16856     },
16857
16858     // private
16859     validateBlur : function(){
16860         return !this.list || !this.list.isVisible();   
16861     },
16862
16863     // private
16864     initQuery : function(){
16865         
16866         var v = this.getRawValue();
16867         
16868         if(this.tickable && this.editable){
16869             v = this.tickableInputEl().getValue();
16870         }
16871         
16872         this.doQuery(v);
16873     },
16874
16875     // private
16876     doForce : function(){
16877         if(this.inputEl().dom.value.length > 0){
16878             this.inputEl().dom.value =
16879                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16880              
16881         }
16882     },
16883
16884     /**
16885      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16886      * query allowing the query action to be canceled if needed.
16887      * @param {String} query The SQL query to execute
16888      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16889      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16890      * saved in the current store (defaults to false)
16891      */
16892     doQuery : function(q, forceAll){
16893         
16894         if(q === undefined || q === null){
16895             q = '';
16896         }
16897         var qe = {
16898             query: q,
16899             forceAll: forceAll,
16900             combo: this,
16901             cancel:false
16902         };
16903         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16904             return false;
16905         }
16906         q = qe.query;
16907         
16908         forceAll = qe.forceAll;
16909         if(forceAll === true || (q.length >= this.minChars)){
16910             
16911             this.hasQuery = true;
16912             
16913             if(this.lastQuery != q || this.alwaysQuery){
16914                 this.lastQuery = q;
16915                 if(this.mode == 'local'){
16916                     this.selectedIndex = -1;
16917                     if(forceAll){
16918                         this.store.clearFilter();
16919                     }else{
16920                         
16921                         if(this.specialFilter){
16922                             this.fireEvent('specialfilter', this);
16923                             this.onLoad();
16924                             return;
16925                         }
16926                         
16927                         this.store.filter(this.displayField, q);
16928                     }
16929                     
16930                     this.store.fireEvent("datachanged", this.store);
16931                     
16932                     this.onLoad();
16933                     
16934                     
16935                 }else{
16936                     
16937                     this.store.baseParams[this.queryParam] = q;
16938                     
16939                     var options = {params : this.getParams(q)};
16940                     
16941                     if(this.loadNext){
16942                         options.add = true;
16943                         options.params.start = this.page * this.pageSize;
16944                     }
16945                     
16946                     this.store.load(options);
16947                     
16948                     /*
16949                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16950                      *  we should expand the list on onLoad
16951                      *  so command out it
16952                      */
16953 //                    this.expand();
16954                 }
16955             }else{
16956                 this.selectedIndex = -1;
16957                 this.onLoad();   
16958             }
16959         }
16960         
16961         this.loadNext = false;
16962     },
16963     
16964     // private
16965     getParams : function(q){
16966         var p = {};
16967         //p[this.queryParam] = q;
16968         
16969         if(this.pageSize){
16970             p.start = 0;
16971             p.limit = this.pageSize;
16972         }
16973         return p;
16974     },
16975
16976     /**
16977      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16978      */
16979     collapse : function(){
16980         if(!this.isExpanded()){
16981             return;
16982         }
16983         
16984         this.list.hide();
16985         
16986         this.hasFocus = false;
16987         
16988         if(this.tickable){
16989             this.okBtn.hide();
16990             this.cancelBtn.hide();
16991             this.trigger.show();
16992             
16993             if(this.editable){
16994                 this.tickableInputEl().dom.value = '';
16995                 this.tickableInputEl().blur();
16996             }
16997             
16998         }
16999         
17000         Roo.get(document).un('mousedown', this.collapseIf, this);
17001         Roo.get(document).un('mousewheel', this.collapseIf, this);
17002         if (!this.editable) {
17003             Roo.get(document).un('keydown', this.listKeyPress, this);
17004         }
17005         this.fireEvent('collapse', this);
17006         
17007         this.validate();
17008     },
17009
17010     // private
17011     collapseIf : function(e){
17012         var in_combo  = e.within(this.el);
17013         var in_list =  e.within(this.list);
17014         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17015         
17016         if (in_combo || in_list || is_list) {
17017             //e.stopPropagation();
17018             return;
17019         }
17020         
17021         if(this.tickable){
17022             this.onTickableFooterButtonClick(e, false, false);
17023         }
17024
17025         this.collapse();
17026         
17027     },
17028
17029     /**
17030      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17031      */
17032     expand : function(){
17033        
17034         if(this.isExpanded() || !this.hasFocus){
17035             return;
17036         }
17037         
17038         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17039         this.list.setWidth(lw);
17040         
17041         Roo.log('expand');
17042         
17043         this.list.show();
17044         
17045         this.restrictHeight();
17046         
17047         if(this.tickable){
17048             
17049             this.tickItems = Roo.apply([], this.item);
17050             
17051             this.okBtn.show();
17052             this.cancelBtn.show();
17053             this.trigger.hide();
17054             
17055             if(this.editable){
17056                 this.tickableInputEl().focus();
17057             }
17058             
17059         }
17060         
17061         Roo.get(document).on('mousedown', this.collapseIf, this);
17062         Roo.get(document).on('mousewheel', this.collapseIf, this);
17063         if (!this.editable) {
17064             Roo.get(document).on('keydown', this.listKeyPress, this);
17065         }
17066         
17067         this.fireEvent('expand', this);
17068     },
17069
17070     // private
17071     // Implements the default empty TriggerField.onTriggerClick function
17072     onTriggerClick : function(e)
17073     {
17074         Roo.log('trigger click');
17075         
17076         if(this.disabled || !this.triggerList){
17077             return;
17078         }
17079         
17080         this.page = 0;
17081         this.loadNext = false;
17082         
17083         if(this.isExpanded()){
17084             this.collapse();
17085             if (!this.blockFocus) {
17086                 this.inputEl().focus();
17087             }
17088             
17089         }else {
17090             this.hasFocus = true;
17091             if(this.triggerAction == 'all') {
17092                 this.doQuery(this.allQuery, true);
17093             } else {
17094                 this.doQuery(this.getRawValue());
17095             }
17096             if (!this.blockFocus) {
17097                 this.inputEl().focus();
17098             }
17099         }
17100     },
17101     
17102     onTickableTriggerClick : function(e)
17103     {
17104         if(this.disabled){
17105             return;
17106         }
17107         
17108         this.page = 0;
17109         this.loadNext = false;
17110         this.hasFocus = true;
17111         
17112         if(this.triggerAction == 'all') {
17113             this.doQuery(this.allQuery, true);
17114         } else {
17115             this.doQuery(this.getRawValue());
17116         }
17117     },
17118     
17119     onSearchFieldClick : function(e)
17120     {
17121         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17122             this.onTickableFooterButtonClick(e, false, false);
17123             return;
17124         }
17125         
17126         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17127             return;
17128         }
17129         
17130         this.page = 0;
17131         this.loadNext = false;
17132         this.hasFocus = true;
17133         
17134         if(this.triggerAction == 'all') {
17135             this.doQuery(this.allQuery, true);
17136         } else {
17137             this.doQuery(this.getRawValue());
17138         }
17139     },
17140     
17141     listKeyPress : function(e)
17142     {
17143         //Roo.log('listkeypress');
17144         // scroll to first matching element based on key pres..
17145         if (e.isSpecialKey()) {
17146             return false;
17147         }
17148         var k = String.fromCharCode(e.getKey()).toUpperCase();
17149         //Roo.log(k);
17150         var match  = false;
17151         var csel = this.view.getSelectedNodes();
17152         var cselitem = false;
17153         if (csel.length) {
17154             var ix = this.view.indexOf(csel[0]);
17155             cselitem  = this.store.getAt(ix);
17156             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17157                 cselitem = false;
17158             }
17159             
17160         }
17161         
17162         this.store.each(function(v) { 
17163             if (cselitem) {
17164                 // start at existing selection.
17165                 if (cselitem.id == v.id) {
17166                     cselitem = false;
17167                 }
17168                 return true;
17169             }
17170                 
17171             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17172                 match = this.store.indexOf(v);
17173                 return false;
17174             }
17175             return true;
17176         }, this);
17177         
17178         if (match === false) {
17179             return true; // no more action?
17180         }
17181         // scroll to?
17182         this.view.select(match);
17183         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17184         sn.scrollIntoView(sn.dom.parentNode, false);
17185     },
17186     
17187     onViewScroll : function(e, t){
17188         
17189         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){
17190             return;
17191         }
17192         
17193         this.hasQuery = true;
17194         
17195         this.loading = this.list.select('.loading', true).first();
17196         
17197         if(this.loading === null){
17198             this.list.createChild({
17199                 tag: 'div',
17200                 cls: 'loading roo-select2-more-results roo-select2-active',
17201                 html: 'Loading more results...'
17202             });
17203             
17204             this.loading = this.list.select('.loading', true).first();
17205             
17206             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17207             
17208             this.loading.hide();
17209         }
17210         
17211         this.loading.show();
17212         
17213         var _combo = this;
17214         
17215         this.page++;
17216         this.loadNext = true;
17217         
17218         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17219         
17220         return;
17221     },
17222     
17223     addItem : function(o)
17224     {   
17225         var dv = ''; // display value
17226         
17227         if (this.displayField) {
17228             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17229         } else {
17230             // this is an error condition!!!
17231             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17232         }
17233         
17234         if(!dv.length){
17235             return;
17236         }
17237         
17238         var choice = this.choices.createChild({
17239             tag: 'li',
17240             cls: 'roo-select2-search-choice',
17241             cn: [
17242                 {
17243                     tag: 'div',
17244                     html: dv
17245                 },
17246                 {
17247                     tag: 'a',
17248                     href: '#',
17249                     cls: 'roo-select2-search-choice-close fa fa-times',
17250                     tabindex: '-1'
17251                 }
17252             ]
17253             
17254         }, this.searchField);
17255         
17256         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17257         
17258         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17259         
17260         this.item.push(o);
17261         
17262         this.lastData = o;
17263         
17264         this.syncValue();
17265         
17266         this.inputEl().dom.value = '';
17267         
17268         this.validate();
17269     },
17270     
17271     onRemoveItem : function(e, _self, o)
17272     {
17273         e.preventDefault();
17274         
17275         this.lastItem = Roo.apply([], this.item);
17276         
17277         var index = this.item.indexOf(o.data) * 1;
17278         
17279         if( index < 0){
17280             Roo.log('not this item?!');
17281             return;
17282         }
17283         
17284         this.item.splice(index, 1);
17285         o.item.remove();
17286         
17287         this.syncValue();
17288         
17289         this.fireEvent('remove', this, e);
17290         
17291         this.validate();
17292         
17293     },
17294     
17295     syncValue : function()
17296     {
17297         if(!this.item.length){
17298             this.clearValue();
17299             return;
17300         }
17301             
17302         var value = [];
17303         var _this = this;
17304         Roo.each(this.item, function(i){
17305             if(_this.valueField){
17306                 value.push(i[_this.valueField]);
17307                 return;
17308             }
17309
17310             value.push(i);
17311         });
17312
17313         this.value = value.join(',');
17314
17315         if(this.hiddenField){
17316             this.hiddenField.dom.value = this.value;
17317         }
17318         
17319         this.store.fireEvent("datachanged", this.store);
17320         
17321         this.validate();
17322     },
17323     
17324     clearItem : function()
17325     {
17326         if(!this.multiple){
17327             return;
17328         }
17329         
17330         this.item = [];
17331         
17332         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17333            c.remove();
17334         });
17335         
17336         this.syncValue();
17337         
17338         this.validate();
17339         
17340         if(this.tickable && !Roo.isTouch){
17341             this.view.refresh();
17342         }
17343     },
17344     
17345     inputEl: function ()
17346     {
17347         if(Roo.isIOS && this.useNativeIOS){
17348             return this.el.select('select.roo-ios-select', true).first();
17349         }
17350         
17351         if(Roo.isTouch && this.mobileTouchView){
17352             return this.el.select('input.form-control',true).first();
17353         }
17354         
17355         if(this.tickable){
17356             return this.searchField;
17357         }
17358         
17359         return this.el.select('input.form-control',true).first();
17360     },
17361     
17362     onTickableFooterButtonClick : function(e, btn, el)
17363     {
17364         e.preventDefault();
17365         
17366         this.lastItem = Roo.apply([], this.item);
17367         
17368         if(btn && btn.name == 'cancel'){
17369             this.tickItems = Roo.apply([], this.item);
17370             this.collapse();
17371             return;
17372         }
17373         
17374         this.clearItem();
17375         
17376         var _this = this;
17377         
17378         Roo.each(this.tickItems, function(o){
17379             _this.addItem(o);
17380         });
17381         
17382         this.collapse();
17383         
17384     },
17385     
17386     validate : function()
17387     {
17388         if(this.getVisibilityEl().hasClass('hidden')){
17389             return true;
17390         }
17391         
17392         var v = this.getRawValue();
17393         
17394         if(this.multiple){
17395             v = this.getValue();
17396         }
17397         
17398         if(this.disabled || this.allowBlank || v.length){
17399             this.markValid();
17400             return true;
17401         }
17402         
17403         this.markInvalid();
17404         return false;
17405     },
17406     
17407     tickableInputEl : function()
17408     {
17409         if(!this.tickable || !this.editable){
17410             return this.inputEl();
17411         }
17412         
17413         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17414     },
17415     
17416     
17417     getAutoCreateTouchView : function()
17418     {
17419         var id = Roo.id();
17420         
17421         var cfg = {
17422             cls: 'form-group' //input-group
17423         };
17424         
17425         var input =  {
17426             tag: 'input',
17427             id : id,
17428             type : this.inputType,
17429             cls : 'form-control x-combo-noedit',
17430             autocomplete: 'new-password',
17431             placeholder : this.placeholder || '',
17432             readonly : true
17433         };
17434         
17435         if (this.name) {
17436             input.name = this.name;
17437         }
17438         
17439         if (this.size) {
17440             input.cls += ' input-' + this.size;
17441         }
17442         
17443         if (this.disabled) {
17444             input.disabled = true;
17445         }
17446         
17447         var inputblock = {
17448             cls : 'roo-combobox-wrap',
17449             cn : [
17450                 input
17451             ]
17452         };
17453         
17454         if(this.before){
17455             inputblock.cls += ' input-group';
17456             
17457             inputblock.cn.unshift({
17458                 tag :'span',
17459                 cls : 'input-group-addon input-group-prepend input-group-text',
17460                 html : this.before
17461             });
17462         }
17463         
17464         if(this.removable && !this.multiple){
17465             inputblock.cls += ' roo-removable';
17466             
17467             inputblock.cn.push({
17468                 tag: 'button',
17469                 html : 'x',
17470                 cls : 'roo-combo-removable-btn close'
17471             });
17472         }
17473
17474         if(this.hasFeedback && !this.allowBlank){
17475             
17476             inputblock.cls += ' has-feedback';
17477             
17478             inputblock.cn.push({
17479                 tag: 'span',
17480                 cls: 'glyphicon form-control-feedback'
17481             });
17482             
17483         }
17484         
17485         if (this.after) {
17486             
17487             inputblock.cls += (this.before) ? '' : ' input-group';
17488             
17489             inputblock.cn.push({
17490                 tag :'span',
17491                 cls : 'input-group-addon input-group-append input-group-text',
17492                 html : this.after
17493             });
17494         }
17495
17496         
17497         var ibwrap = inputblock;
17498         
17499         if(this.multiple){
17500             ibwrap = {
17501                 tag: 'ul',
17502                 cls: 'roo-select2-choices',
17503                 cn:[
17504                     {
17505                         tag: 'li',
17506                         cls: 'roo-select2-search-field',
17507                         cn: [
17508
17509                             inputblock
17510                         ]
17511                     }
17512                 ]
17513             };
17514         
17515             
17516         }
17517         
17518         var combobox = {
17519             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17520             cn: [
17521                 {
17522                     tag: 'input',
17523                     type : 'hidden',
17524                     cls: 'form-hidden-field'
17525                 },
17526                 ibwrap
17527             ]
17528         };
17529         
17530         if(!this.multiple && this.showToggleBtn){
17531             
17532             var caret = {
17533                 cls: 'caret'
17534             };
17535             
17536             if (this.caret != false) {
17537                 caret = {
17538                      tag: 'i',
17539                      cls: 'fa fa-' + this.caret
17540                 };
17541                 
17542             }
17543             
17544             combobox.cn.push({
17545                 tag :'span',
17546                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17547                 cn : [
17548                     Roo.bootstrap.version == 3 ? caret : '',
17549                     {
17550                         tag: 'span',
17551                         cls: 'combobox-clear',
17552                         cn  : [
17553                             {
17554                                 tag : 'i',
17555                                 cls: 'icon-remove'
17556                             }
17557                         ]
17558                     }
17559                 ]
17560
17561             })
17562         }
17563         
17564         if(this.multiple){
17565             combobox.cls += ' roo-select2-container-multi';
17566         }
17567         
17568         var align = this.labelAlign || this.parentLabelAlign();
17569         
17570         if (align ==='left' && this.fieldLabel.length) {
17571
17572             cfg.cn = [
17573                 {
17574                    tag : 'i',
17575                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17576                    tooltip : 'This field is required'
17577                 },
17578                 {
17579                     tag: 'label',
17580                     cls : 'control-label col-form-label',
17581                     html : this.fieldLabel
17582
17583                 },
17584                 {
17585                     cls : 'roo-combobox-wrap ', 
17586                     cn: [
17587                         combobox
17588                     ]
17589                 }
17590             ];
17591             
17592             var labelCfg = cfg.cn[1];
17593             var contentCfg = cfg.cn[2];
17594             
17595
17596             if(this.indicatorpos == 'right'){
17597                 cfg.cn = [
17598                     {
17599                         tag: 'label',
17600                         'for' :  id,
17601                         cls : 'control-label col-form-label',
17602                         cn : [
17603                             {
17604                                 tag : 'span',
17605                                 html : this.fieldLabel
17606                             },
17607                             {
17608                                 tag : 'i',
17609                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17610                                 tooltip : 'This field is required'
17611                             }
17612                         ]
17613                     },
17614                     {
17615                         cls : "roo-combobox-wrap ",
17616                         cn: [
17617                             combobox
17618                         ]
17619                     }
17620
17621                 ];
17622                 
17623                 labelCfg = cfg.cn[0];
17624                 contentCfg = cfg.cn[1];
17625             }
17626             
17627            
17628             
17629             if(this.labelWidth > 12){
17630                 labelCfg.style = "width: " + this.labelWidth + 'px';
17631             }
17632            
17633             if(this.labelWidth < 13 && this.labelmd == 0){
17634                 this.labelmd = this.labelWidth;
17635             }
17636             
17637             if(this.labellg > 0){
17638                 labelCfg.cls += ' col-lg-' + this.labellg;
17639                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17640             }
17641             
17642             if(this.labelmd > 0){
17643                 labelCfg.cls += ' col-md-' + this.labelmd;
17644                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17645             }
17646             
17647             if(this.labelsm > 0){
17648                 labelCfg.cls += ' col-sm-' + this.labelsm;
17649                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17650             }
17651             
17652             if(this.labelxs > 0){
17653                 labelCfg.cls += ' col-xs-' + this.labelxs;
17654                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17655             }
17656                 
17657                 
17658         } else if ( this.fieldLabel.length) {
17659             cfg.cn = [
17660                 {
17661                    tag : 'i',
17662                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17663                    tooltip : 'This field is required'
17664                 },
17665                 {
17666                     tag: 'label',
17667                     cls : 'control-label',
17668                     html : this.fieldLabel
17669
17670                 },
17671                 {
17672                     cls : '', 
17673                     cn: [
17674                         combobox
17675                     ]
17676                 }
17677             ];
17678             
17679             if(this.indicatorpos == 'right'){
17680                 cfg.cn = [
17681                     {
17682                         tag: 'label',
17683                         cls : 'control-label',
17684                         html : this.fieldLabel,
17685                         cn : [
17686                             {
17687                                tag : 'i',
17688                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17689                                tooltip : 'This field is required'
17690                             }
17691                         ]
17692                     },
17693                     {
17694                         cls : '', 
17695                         cn: [
17696                             combobox
17697                         ]
17698                     }
17699                 ];
17700             }
17701         } else {
17702             cfg.cn = combobox;    
17703         }
17704         
17705         
17706         var settings = this;
17707         
17708         ['xs','sm','md','lg'].map(function(size){
17709             if (settings[size]) {
17710                 cfg.cls += ' col-' + size + '-' + settings[size];
17711             }
17712         });
17713         
17714         return cfg;
17715     },
17716     
17717     initTouchView : function()
17718     {
17719         this.renderTouchView();
17720         
17721         this.touchViewEl.on('scroll', function(){
17722             this.el.dom.scrollTop = 0;
17723         }, this);
17724         
17725         this.originalValue = this.getValue();
17726         
17727         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17728         
17729         this.inputEl().on("click", this.showTouchView, this);
17730         if (this.triggerEl) {
17731             this.triggerEl.on("click", this.showTouchView, this);
17732         }
17733         
17734         
17735         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17736         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17737         
17738         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17739         
17740         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17741         this.store.on('load', this.onTouchViewLoad, this);
17742         this.store.on('loadexception', this.onTouchViewLoadException, this);
17743         
17744         if(this.hiddenName){
17745             
17746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17747             
17748             this.hiddenField.dom.value =
17749                 this.hiddenValue !== undefined ? this.hiddenValue :
17750                 this.value !== undefined ? this.value : '';
17751         
17752             this.el.dom.removeAttribute('name');
17753             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17754         }
17755         
17756         if(this.multiple){
17757             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17759         }
17760         
17761         if(this.removable && !this.multiple){
17762             var close = this.closeTriggerEl();
17763             if(close){
17764                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17765                 close.on('click', this.removeBtnClick, this, close);
17766             }
17767         }
17768         /*
17769          * fix the bug in Safari iOS8
17770          */
17771         this.inputEl().on("focus", function(e){
17772             document.activeElement.blur();
17773         }, this);
17774         
17775         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17776         
17777         return;
17778         
17779         
17780     },
17781     
17782     renderTouchView : function()
17783     {
17784         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17785         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17786         
17787         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17788         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17789         
17790         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17791         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17792         this.touchViewBodyEl.setStyle('overflow', 'auto');
17793         
17794         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17795         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17796         
17797         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17798         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799         
17800     },
17801     
17802     showTouchView : function()
17803     {
17804         if(this.disabled){
17805             return;
17806         }
17807         
17808         this.touchViewHeaderEl.hide();
17809
17810         if(this.modalTitle.length){
17811             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17812             this.touchViewHeaderEl.show();
17813         }
17814
17815         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17816         this.touchViewEl.show();
17817
17818         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17819         
17820         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17821         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17822
17823         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17824
17825         if(this.modalTitle.length){
17826             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17827         }
17828         
17829         this.touchViewBodyEl.setHeight(bodyHeight);
17830
17831         if(this.animate){
17832             var _this = this;
17833             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17834         }else{
17835             this.touchViewEl.addClass(['in','show']);
17836         }
17837         
17838         if(this._touchViewMask){
17839             Roo.get(document.body).addClass("x-body-masked");
17840             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17841             this._touchViewMask.setStyle('z-index', 10000);
17842             this._touchViewMask.addClass('show');
17843         }
17844         
17845         this.doTouchViewQuery();
17846         
17847     },
17848     
17849     hideTouchView : function()
17850     {
17851         this.touchViewEl.removeClass(['in','show']);
17852
17853         if(this.animate){
17854             var _this = this;
17855             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17856         }else{
17857             this.touchViewEl.setStyle('display', 'none');
17858         }
17859         
17860         if(this._touchViewMask){
17861             this._touchViewMask.removeClass('show');
17862             Roo.get(document.body).removeClass("x-body-masked");
17863         }
17864     },
17865     
17866     setTouchViewValue : function()
17867     {
17868         if(this.multiple){
17869             this.clearItem();
17870         
17871             var _this = this;
17872
17873             Roo.each(this.tickItems, function(o){
17874                 this.addItem(o);
17875             }, this);
17876         }
17877         
17878         this.hideTouchView();
17879     },
17880     
17881     doTouchViewQuery : function()
17882     {
17883         var qe = {
17884             query: '',
17885             forceAll: true,
17886             combo: this,
17887             cancel:false
17888         };
17889         
17890         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17891             return false;
17892         }
17893         
17894         if(!this.alwaysQuery || this.mode == 'local'){
17895             this.onTouchViewLoad();
17896             return;
17897         }
17898         
17899         this.store.load();
17900     },
17901     
17902     onTouchViewBeforeLoad : function(combo,opts)
17903     {
17904         return;
17905     },
17906
17907     // private
17908     onTouchViewLoad : function()
17909     {
17910         if(this.store.getCount() < 1){
17911             this.onTouchViewEmptyResults();
17912             return;
17913         }
17914         
17915         this.clearTouchView();
17916         
17917         var rawValue = this.getRawValue();
17918         
17919         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17920         
17921         this.tickItems = [];
17922         
17923         this.store.data.each(function(d, rowIndex){
17924             var row = this.touchViewListGroup.createChild(template);
17925             
17926             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17927                 row.addClass(d.data.cls);
17928             }
17929             
17930             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17931                 var cfg = {
17932                     data : d.data,
17933                     html : d.data[this.displayField]
17934                 };
17935                 
17936                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17937                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17938                 }
17939             }
17940             row.removeClass('selected');
17941             if(!this.multiple && this.valueField &&
17942                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17943             {
17944                 // radio buttons..
17945                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17946                 row.addClass('selected');
17947             }
17948             
17949             if(this.multiple && this.valueField &&
17950                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17951             {
17952                 
17953                 // checkboxes...
17954                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955                 this.tickItems.push(d.data);
17956             }
17957             
17958             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17959             
17960         }, this);
17961         
17962         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17963         
17964         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17965
17966         if(this.modalTitle.length){
17967             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17968         }
17969
17970         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17971         
17972         if(this.mobile_restrict_height && listHeight < bodyHeight){
17973             this.touchViewBodyEl.setHeight(listHeight);
17974         }
17975         
17976         var _this = this;
17977         
17978         if(firstChecked && listHeight > bodyHeight){
17979             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17980         }
17981         
17982     },
17983     
17984     onTouchViewLoadException : function()
17985     {
17986         this.hideTouchView();
17987     },
17988     
17989     onTouchViewEmptyResults : function()
17990     {
17991         this.clearTouchView();
17992         
17993         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17994         
17995         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17996         
17997     },
17998     
17999     clearTouchView : function()
18000     {
18001         this.touchViewListGroup.dom.innerHTML = '';
18002     },
18003     
18004     onTouchViewClick : function(e, el, o)
18005     {
18006         e.preventDefault();
18007         
18008         var row = o.row;
18009         var rowIndex = o.rowIndex;
18010         
18011         var r = this.store.getAt(rowIndex);
18012         
18013         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18014             
18015             if(!this.multiple){
18016                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18017                     c.dom.removeAttribute('checked');
18018                 }, this);
18019
18020                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18021
18022                 this.setFromData(r.data);
18023
18024                 var close = this.closeTriggerEl();
18025
18026                 if(close){
18027                     close.show();
18028                 }
18029
18030                 this.hideTouchView();
18031
18032                 this.fireEvent('select', this, r, rowIndex);
18033
18034                 return;
18035             }
18036
18037             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18038                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18039                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18040                 return;
18041             }
18042
18043             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044             this.addItem(r.data);
18045             this.tickItems.push(r.data);
18046         }
18047     },
18048     
18049     getAutoCreateNativeIOS : function()
18050     {
18051         var cfg = {
18052             cls: 'form-group' //input-group,
18053         };
18054         
18055         var combobox =  {
18056             tag: 'select',
18057             cls : 'roo-ios-select'
18058         };
18059         
18060         if (this.name) {
18061             combobox.name = this.name;
18062         }
18063         
18064         if (this.disabled) {
18065             combobox.disabled = true;
18066         }
18067         
18068         var settings = this;
18069         
18070         ['xs','sm','md','lg'].map(function(size){
18071             if (settings[size]) {
18072                 cfg.cls += ' col-' + size + '-' + settings[size];
18073             }
18074         });
18075         
18076         cfg.cn = combobox;
18077         
18078         return cfg;
18079         
18080     },
18081     
18082     initIOSView : function()
18083     {
18084         this.store.on('load', this.onIOSViewLoad, this);
18085         
18086         return;
18087     },
18088     
18089     onIOSViewLoad : function()
18090     {
18091         if(this.store.getCount() < 1){
18092             return;
18093         }
18094         
18095         this.clearIOSView();
18096         
18097         if(this.allowBlank) {
18098             
18099             var default_text = '-- SELECT --';
18100             
18101             if(this.placeholder.length){
18102                 default_text = this.placeholder;
18103             }
18104             
18105             if(this.emptyTitle.length){
18106                 default_text += ' - ' + this.emptyTitle + ' -';
18107             }
18108             
18109             var opt = this.inputEl().createChild({
18110                 tag: 'option',
18111                 value : 0,
18112                 html : default_text
18113             });
18114             
18115             var o = {};
18116             o[this.valueField] = 0;
18117             o[this.displayField] = default_text;
18118             
18119             this.ios_options.push({
18120                 data : o,
18121                 el : opt
18122             });
18123             
18124         }
18125         
18126         this.store.data.each(function(d, rowIndex){
18127             
18128             var html = '';
18129             
18130             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18131                 html = d.data[this.displayField];
18132             }
18133             
18134             var value = '';
18135             
18136             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18137                 value = d.data[this.valueField];
18138             }
18139             
18140             var option = {
18141                 tag: 'option',
18142                 value : value,
18143                 html : html
18144             };
18145             
18146             if(this.value == d.data[this.valueField]){
18147                 option['selected'] = true;
18148             }
18149             
18150             var opt = this.inputEl().createChild(option);
18151             
18152             this.ios_options.push({
18153                 data : d.data,
18154                 el : opt
18155             });
18156             
18157         }, this);
18158         
18159         this.inputEl().on('change', function(){
18160            this.fireEvent('select', this);
18161         }, this);
18162         
18163     },
18164     
18165     clearIOSView: function()
18166     {
18167         this.inputEl().dom.innerHTML = '';
18168         
18169         this.ios_options = [];
18170     },
18171     
18172     setIOSValue: function(v)
18173     {
18174         this.value = v;
18175         
18176         if(!this.ios_options){
18177             return;
18178         }
18179         
18180         Roo.each(this.ios_options, function(opts){
18181            
18182            opts.el.dom.removeAttribute('selected');
18183            
18184            if(opts.data[this.valueField] != v){
18185                return;
18186            }
18187            
18188            opts.el.dom.setAttribute('selected', true);
18189            
18190         }, this);
18191     }
18192
18193     /** 
18194     * @cfg {Boolean} grow 
18195     * @hide 
18196     */
18197     /** 
18198     * @cfg {Number} growMin 
18199     * @hide 
18200     */
18201     /** 
18202     * @cfg {Number} growMax 
18203     * @hide 
18204     */
18205     /**
18206      * @hide
18207      * @method autoSize
18208      */
18209 });
18210
18211 Roo.apply(Roo.bootstrap.ComboBox,  {
18212     
18213     header : {
18214         tag: 'div',
18215         cls: 'modal-header',
18216         cn: [
18217             {
18218                 tag: 'h4',
18219                 cls: 'modal-title'
18220             }
18221         ]
18222     },
18223     
18224     body : {
18225         tag: 'div',
18226         cls: 'modal-body',
18227         cn: [
18228             {
18229                 tag: 'ul',
18230                 cls: 'list-group'
18231             }
18232         ]
18233     },
18234     
18235     listItemRadio : {
18236         tag: 'li',
18237         cls: 'list-group-item',
18238         cn: [
18239             {
18240                 tag: 'span',
18241                 cls: 'roo-combobox-list-group-item-value'
18242             },
18243             {
18244                 tag: 'div',
18245                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18246                 cn: [
18247                     {
18248                         tag: 'input',
18249                         type: 'radio'
18250                     },
18251                     {
18252                         tag: 'label'
18253                     }
18254                 ]
18255             }
18256         ]
18257     },
18258     
18259     listItemCheckbox : {
18260         tag: 'li',
18261         cls: 'list-group-item',
18262         cn: [
18263             {
18264                 tag: 'span',
18265                 cls: 'roo-combobox-list-group-item-value'
18266             },
18267             {
18268                 tag: 'div',
18269                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18270                 cn: [
18271                     {
18272                         tag: 'input',
18273                         type: 'checkbox'
18274                     },
18275                     {
18276                         tag: 'label'
18277                     }
18278                 ]
18279             }
18280         ]
18281     },
18282     
18283     emptyResult : {
18284         tag: 'div',
18285         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18286     },
18287     
18288     footer : {
18289         tag: 'div',
18290         cls: 'modal-footer',
18291         cn: [
18292             {
18293                 tag: 'div',
18294                 cls: 'row',
18295                 cn: [
18296                     {
18297                         tag: 'div',
18298                         cls: 'col-xs-6 text-left',
18299                         cn: {
18300                             tag: 'button',
18301                             cls: 'btn btn-danger roo-touch-view-cancel',
18302                             html: 'Cancel'
18303                         }
18304                     },
18305                     {
18306                         tag: 'div',
18307                         cls: 'col-xs-6 text-right',
18308                         cn: {
18309                             tag: 'button',
18310                             cls: 'btn btn-success roo-touch-view-ok',
18311                             html: 'OK'
18312                         }
18313                     }
18314                 ]
18315             }
18316         ]
18317         
18318     }
18319 });
18320
18321 Roo.apply(Roo.bootstrap.ComboBox,  {
18322     
18323     touchViewTemplate : {
18324         tag: 'div',
18325         cls: 'modal fade roo-combobox-touch-view',
18326         cn: [
18327             {
18328                 tag: 'div',
18329                 cls: 'modal-dialog',
18330                 style : 'position:fixed', // we have to fix position....
18331                 cn: [
18332                     {
18333                         tag: 'div',
18334                         cls: 'modal-content',
18335                         cn: [
18336                             Roo.bootstrap.ComboBox.header,
18337                             Roo.bootstrap.ComboBox.body,
18338                             Roo.bootstrap.ComboBox.footer
18339                         ]
18340                     }
18341                 ]
18342             }
18343         ]
18344     }
18345 });/*
18346  * Based on:
18347  * Ext JS Library 1.1.1
18348  * Copyright(c) 2006-2007, Ext JS, LLC.
18349  *
18350  * Originally Released Under LGPL - original licence link has changed is not relivant.
18351  *
18352  * Fork - LGPL
18353  * <script type="text/javascript">
18354  */
18355
18356 /**
18357  * @class Roo.View
18358  * @extends Roo.util.Observable
18359  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18360  * This class also supports single and multi selection modes. <br>
18361  * Create a data model bound view:
18362  <pre><code>
18363  var store = new Roo.data.Store(...);
18364
18365  var view = new Roo.View({
18366     el : "my-element",
18367     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18368  
18369     singleSelect: true,
18370     selectedClass: "ydataview-selected",
18371     store: store
18372  });
18373
18374  // listen for node click?
18375  view.on("click", function(vw, index, node, e){
18376  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18377  });
18378
18379  // load XML data
18380  dataModel.load("foobar.xml");
18381  </code></pre>
18382  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18383  * <br><br>
18384  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18385  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18386  * 
18387  * Note: old style constructor is still suported (container, template, config)
18388  * 
18389  * @constructor
18390  * Create a new View
18391  * @param {Object} config The config object
18392  * 
18393  */
18394 Roo.View = function(config, depreciated_tpl, depreciated_config){
18395     
18396     this.parent = false;
18397     
18398     if (typeof(depreciated_tpl) == 'undefined') {
18399         // new way.. - universal constructor.
18400         Roo.apply(this, config);
18401         this.el  = Roo.get(this.el);
18402     } else {
18403         // old format..
18404         this.el  = Roo.get(config);
18405         this.tpl = depreciated_tpl;
18406         Roo.apply(this, depreciated_config);
18407     }
18408     this.wrapEl  = this.el.wrap().wrap();
18409     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18410     
18411     
18412     if(typeof(this.tpl) == "string"){
18413         this.tpl = new Roo.Template(this.tpl);
18414     } else {
18415         // support xtype ctors..
18416         this.tpl = new Roo.factory(this.tpl, Roo);
18417     }
18418     
18419     
18420     this.tpl.compile();
18421     
18422     /** @private */
18423     this.addEvents({
18424         /**
18425          * @event beforeclick
18426          * Fires before a click is processed. Returns false to cancel the default action.
18427          * @param {Roo.View} this
18428          * @param {Number} index The index of the target node
18429          * @param {HTMLElement} node The target node
18430          * @param {Roo.EventObject} e The raw event object
18431          */
18432             "beforeclick" : true,
18433         /**
18434          * @event click
18435          * Fires when a template node is clicked.
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             "click" : true,
18442         /**
18443          * @event dblclick
18444          * Fires when a template node is double 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             "dblclick" : true,
18451         /**
18452          * @event contextmenu
18453          * Fires when a template node is right 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             "contextmenu" : true,
18460         /**
18461          * @event selectionchange
18462          * Fires when the selected nodes change.
18463          * @param {Roo.View} this
18464          * @param {Array} selections Array of the selected nodes
18465          */
18466             "selectionchange" : true,
18467     
18468         /**
18469          * @event beforeselect
18470          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18471          * @param {Roo.View} this
18472          * @param {HTMLElement} node The node to be selected
18473          * @param {Array} selections Array of currently selected nodes
18474          */
18475             "beforeselect" : true,
18476         /**
18477          * @event preparedata
18478          * Fires on every row to render, to allow you to change the data.
18479          * @param {Roo.View} this
18480          * @param {Object} data to be rendered (change this)
18481          */
18482           "preparedata" : true
18483           
18484           
18485         });
18486
18487
18488
18489     this.el.on({
18490         "click": this.onClick,
18491         "dblclick": this.onDblClick,
18492         "contextmenu": this.onContextMenu,
18493         scope:this
18494     });
18495
18496     this.selections = [];
18497     this.nodes = [];
18498     this.cmp = new Roo.CompositeElementLite([]);
18499     if(this.store){
18500         this.store = Roo.factory(this.store, Roo.data);
18501         this.setStore(this.store, true);
18502     }
18503     
18504     if ( this.footer && this.footer.xtype) {
18505            
18506          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18507         
18508         this.footer.dataSource = this.store;
18509         this.footer.container = fctr;
18510         this.footer = Roo.factory(this.footer, Roo);
18511         fctr.insertFirst(this.el);
18512         
18513         // this is a bit insane - as the paging toolbar seems to detach the el..
18514 //        dom.parentNode.parentNode.parentNode
18515          // they get detached?
18516     }
18517     
18518     
18519     Roo.View.superclass.constructor.call(this);
18520     
18521     
18522 };
18523
18524 Roo.extend(Roo.View, Roo.util.Observable, {
18525     
18526      /**
18527      * @cfg {Roo.data.Store} store Data store to load data from.
18528      */
18529     store : false,
18530     
18531     /**
18532      * @cfg {String|Roo.Element} el The container element.
18533      */
18534     el : '',
18535     
18536     /**
18537      * @cfg {String|Roo.Template} tpl The template used by this View 
18538      */
18539     tpl : false,
18540     /**
18541      * @cfg {String} dataName the named area of the template to use as the data area
18542      *                          Works with domtemplates roo-name="name"
18543      */
18544     dataName: false,
18545     /**
18546      * @cfg {String} selectedClass The css class to add to selected nodes
18547      */
18548     selectedClass : "x-view-selected",
18549      /**
18550      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18551      */
18552     emptyText : "",
18553     
18554     /**
18555      * @cfg {String} text to display on mask (default Loading)
18556      */
18557     mask : false,
18558     /**
18559      * @cfg {Boolean} multiSelect Allow multiple selection
18560      */
18561     multiSelect : false,
18562     /**
18563      * @cfg {Boolean} singleSelect Allow single selection
18564      */
18565     singleSelect:  false,
18566     
18567     /**
18568      * @cfg {Boolean} toggleSelect - selecting 
18569      */
18570     toggleSelect : false,
18571     
18572     /**
18573      * @cfg {Boolean} tickable - selecting 
18574      */
18575     tickable : false,
18576     
18577     /**
18578      * Returns the element this view is bound to.
18579      * @return {Roo.Element}
18580      */
18581     getEl : function(){
18582         return this.wrapEl;
18583     },
18584     
18585     
18586
18587     /**
18588      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18589      */
18590     refresh : function(){
18591         //Roo.log('refresh');
18592         var t = this.tpl;
18593         
18594         // if we are using something like 'domtemplate', then
18595         // the what gets used is:
18596         // t.applySubtemplate(NAME, data, wrapping data..)
18597         // the outer template then get' applied with
18598         //     the store 'extra data'
18599         // and the body get's added to the
18600         //      roo-name="data" node?
18601         //      <span class='roo-tpl-{name}'></span> ?????
18602         
18603         
18604         
18605         this.clearSelections();
18606         this.el.update("");
18607         var html = [];
18608         var records = this.store.getRange();
18609         if(records.length < 1) {
18610             
18611             // is this valid??  = should it render a template??
18612             
18613             this.el.update(this.emptyText);
18614             return;
18615         }
18616         var el = this.el;
18617         if (this.dataName) {
18618             this.el.update(t.apply(this.store.meta)); //????
18619             el = this.el.child('.roo-tpl-' + this.dataName);
18620         }
18621         
18622         for(var i = 0, len = records.length; i < len; i++){
18623             var data = this.prepareData(records[i].data, i, records[i]);
18624             this.fireEvent("preparedata", this, data, i, records[i]);
18625             
18626             var d = Roo.apply({}, data);
18627             
18628             if(this.tickable){
18629                 Roo.apply(d, {'roo-id' : Roo.id()});
18630                 
18631                 var _this = this;
18632             
18633                 Roo.each(this.parent.item, function(item){
18634                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18635                         return;
18636                     }
18637                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18638                 });
18639             }
18640             
18641             html[html.length] = Roo.util.Format.trim(
18642                 this.dataName ?
18643                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18644                     t.apply(d)
18645             );
18646         }
18647         
18648         
18649         
18650         el.update(html.join(""));
18651         this.nodes = el.dom.childNodes;
18652         this.updateIndexes(0);
18653     },
18654     
18655
18656     /**
18657      * Function to override to reformat the data that is sent to
18658      * the template for each node.
18659      * DEPRICATED - use the preparedata event handler.
18660      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18661      * a JSON object for an UpdateManager bound view).
18662      */
18663     prepareData : function(data, index, record)
18664     {
18665         this.fireEvent("preparedata", this, data, index, record);
18666         return data;
18667     },
18668
18669     onUpdate : function(ds, record){
18670         // Roo.log('on update');   
18671         this.clearSelections();
18672         var index = this.store.indexOf(record);
18673         var n = this.nodes[index];
18674         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18675         n.parentNode.removeChild(n);
18676         this.updateIndexes(index, index);
18677     },
18678
18679     
18680     
18681 // --------- FIXME     
18682     onAdd : function(ds, records, index)
18683     {
18684         //Roo.log(['on Add', ds, records, index] );        
18685         this.clearSelections();
18686         if(this.nodes.length == 0){
18687             this.refresh();
18688             return;
18689         }
18690         var n = this.nodes[index];
18691         for(var i = 0, len = records.length; i < len; i++){
18692             var d = this.prepareData(records[i].data, i, records[i]);
18693             if(n){
18694                 this.tpl.insertBefore(n, d);
18695             }else{
18696                 
18697                 this.tpl.append(this.el, d);
18698             }
18699         }
18700         this.updateIndexes(index);
18701     },
18702
18703     onRemove : function(ds, record, index){
18704        // Roo.log('onRemove');
18705         this.clearSelections();
18706         var el = this.dataName  ?
18707             this.el.child('.roo-tpl-' + this.dataName) :
18708             this.el; 
18709         
18710         el.dom.removeChild(this.nodes[index]);
18711         this.updateIndexes(index);
18712     },
18713
18714     /**
18715      * Refresh an individual node.
18716      * @param {Number} index
18717      */
18718     refreshNode : function(index){
18719         this.onUpdate(this.store, this.store.getAt(index));
18720     },
18721
18722     updateIndexes : function(startIndex, endIndex){
18723         var ns = this.nodes;
18724         startIndex = startIndex || 0;
18725         endIndex = endIndex || ns.length - 1;
18726         for(var i = startIndex; i <= endIndex; i++){
18727             ns[i].nodeIndex = i;
18728         }
18729     },
18730
18731     /**
18732      * Changes the data store this view uses and refresh the view.
18733      * @param {Store} store
18734      */
18735     setStore : function(store, initial){
18736         if(!initial && this.store){
18737             this.store.un("datachanged", this.refresh);
18738             this.store.un("add", this.onAdd);
18739             this.store.un("remove", this.onRemove);
18740             this.store.un("update", this.onUpdate);
18741             this.store.un("clear", this.refresh);
18742             this.store.un("beforeload", this.onBeforeLoad);
18743             this.store.un("load", this.onLoad);
18744             this.store.un("loadexception", this.onLoad);
18745         }
18746         if(store){
18747           
18748             store.on("datachanged", this.refresh, this);
18749             store.on("add", this.onAdd, this);
18750             store.on("remove", this.onRemove, this);
18751             store.on("update", this.onUpdate, this);
18752             store.on("clear", this.refresh, this);
18753             store.on("beforeload", this.onBeforeLoad, this);
18754             store.on("load", this.onLoad, this);
18755             store.on("loadexception", this.onLoad, this);
18756         }
18757         
18758         if(store){
18759             this.refresh();
18760         }
18761     },
18762     /**
18763      * onbeforeLoad - masks the loading area.
18764      *
18765      */
18766     onBeforeLoad : function(store,opts)
18767     {
18768          //Roo.log('onBeforeLoad');   
18769         if (!opts.add) {
18770             this.el.update("");
18771         }
18772         this.el.mask(this.mask ? this.mask : "Loading" ); 
18773     },
18774     onLoad : function ()
18775     {
18776         this.el.unmask();
18777     },
18778     
18779
18780     /**
18781      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18782      * @param {HTMLElement} node
18783      * @return {HTMLElement} The template node
18784      */
18785     findItemFromChild : function(node){
18786         var el = this.dataName  ?
18787             this.el.child('.roo-tpl-' + this.dataName,true) :
18788             this.el.dom; 
18789         
18790         if(!node || node.parentNode == el){
18791                     return node;
18792             }
18793             var p = node.parentNode;
18794             while(p && p != el){
18795             if(p.parentNode == el){
18796                 return p;
18797             }
18798             p = p.parentNode;
18799         }
18800             return null;
18801     },
18802
18803     /** @ignore */
18804     onClick : function(e){
18805         var item = this.findItemFromChild(e.getTarget());
18806         if(item){
18807             var index = this.indexOf(item);
18808             if(this.onItemClick(item, index, e) !== false){
18809                 this.fireEvent("click", this, index, item, e);
18810             }
18811         }else{
18812             this.clearSelections();
18813         }
18814     },
18815
18816     /** @ignore */
18817     onContextMenu : function(e){
18818         var item = this.findItemFromChild(e.getTarget());
18819         if(item){
18820             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18821         }
18822     },
18823
18824     /** @ignore */
18825     onDblClick : function(e){
18826         var item = this.findItemFromChild(e.getTarget());
18827         if(item){
18828             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18829         }
18830     },
18831
18832     onItemClick : function(item, index, e)
18833     {
18834         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18835             return false;
18836         }
18837         if (this.toggleSelect) {
18838             var m = this.isSelected(item) ? 'unselect' : 'select';
18839             //Roo.log(m);
18840             var _t = this;
18841             _t[m](item, true, false);
18842             return true;
18843         }
18844         if(this.multiSelect || this.singleSelect){
18845             if(this.multiSelect && e.shiftKey && this.lastSelection){
18846                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18847             }else{
18848                 this.select(item, this.multiSelect && e.ctrlKey);
18849                 this.lastSelection = item;
18850             }
18851             
18852             if(!this.tickable){
18853                 e.preventDefault();
18854             }
18855             
18856         }
18857         return true;
18858     },
18859
18860     /**
18861      * Get the number of selected nodes.
18862      * @return {Number}
18863      */
18864     getSelectionCount : function(){
18865         return this.selections.length;
18866     },
18867
18868     /**
18869      * Get the currently selected nodes.
18870      * @return {Array} An array of HTMLElements
18871      */
18872     getSelectedNodes : function(){
18873         return this.selections;
18874     },
18875
18876     /**
18877      * Get the indexes of the selected nodes.
18878      * @return {Array}
18879      */
18880     getSelectedIndexes : function(){
18881         var indexes = [], s = this.selections;
18882         for(var i = 0, len = s.length; i < len; i++){
18883             indexes.push(s[i].nodeIndex);
18884         }
18885         return indexes;
18886     },
18887
18888     /**
18889      * Clear all selections
18890      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18891      */
18892     clearSelections : function(suppressEvent){
18893         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18894             this.cmp.elements = this.selections;
18895             this.cmp.removeClass(this.selectedClass);
18896             this.selections = [];
18897             if(!suppressEvent){
18898                 this.fireEvent("selectionchange", this, this.selections);
18899             }
18900         }
18901     },
18902
18903     /**
18904      * Returns true if the passed node is selected
18905      * @param {HTMLElement/Number} node The node or node index
18906      * @return {Boolean}
18907      */
18908     isSelected : function(node){
18909         var s = this.selections;
18910         if(s.length < 1){
18911             return false;
18912         }
18913         node = this.getNode(node);
18914         return s.indexOf(node) !== -1;
18915     },
18916
18917     /**
18918      * Selects nodes.
18919      * @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
18920      * @param {Boolean} keepExisting (optional) true to keep existing selections
18921      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18922      */
18923     select : function(nodeInfo, keepExisting, suppressEvent){
18924         if(nodeInfo instanceof Array){
18925             if(!keepExisting){
18926                 this.clearSelections(true);
18927             }
18928             for(var i = 0, len = nodeInfo.length; i < len; i++){
18929                 this.select(nodeInfo[i], true, true);
18930             }
18931             return;
18932         } 
18933         var node = this.getNode(nodeInfo);
18934         if(!node || this.isSelected(node)){
18935             return; // already selected.
18936         }
18937         if(!keepExisting){
18938             this.clearSelections(true);
18939         }
18940         
18941         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18942             Roo.fly(node).addClass(this.selectedClass);
18943             this.selections.push(node);
18944             if(!suppressEvent){
18945                 this.fireEvent("selectionchange", this, this.selections);
18946             }
18947         }
18948         
18949         
18950     },
18951       /**
18952      * Unselects nodes.
18953      * @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
18954      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18955      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18956      */
18957     unselect : function(nodeInfo, keepExisting, suppressEvent)
18958     {
18959         if(nodeInfo instanceof Array){
18960             Roo.each(this.selections, function(s) {
18961                 this.unselect(s, nodeInfo);
18962             }, this);
18963             return;
18964         }
18965         var node = this.getNode(nodeInfo);
18966         if(!node || !this.isSelected(node)){
18967             //Roo.log("not selected");
18968             return; // not selected.
18969         }
18970         // fireevent???
18971         var ns = [];
18972         Roo.each(this.selections, function(s) {
18973             if (s == node ) {
18974                 Roo.fly(node).removeClass(this.selectedClass);
18975
18976                 return;
18977             }
18978             ns.push(s);
18979         },this);
18980         
18981         this.selections= ns;
18982         this.fireEvent("selectionchange", this, this.selections);
18983     },
18984
18985     /**
18986      * Gets a template node.
18987      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988      * @return {HTMLElement} The node or null if it wasn't found
18989      */
18990     getNode : function(nodeInfo){
18991         if(typeof nodeInfo == "string"){
18992             return document.getElementById(nodeInfo);
18993         }else if(typeof nodeInfo == "number"){
18994             return this.nodes[nodeInfo];
18995         }
18996         return nodeInfo;
18997     },
18998
18999     /**
19000      * Gets a range template nodes.
19001      * @param {Number} startIndex
19002      * @param {Number} endIndex
19003      * @return {Array} An array of nodes
19004      */
19005     getNodes : function(start, end){
19006         var ns = this.nodes;
19007         start = start || 0;
19008         end = typeof end == "undefined" ? ns.length - 1 : end;
19009         var nodes = [];
19010         if(start <= end){
19011             for(var i = start; i <= end; i++){
19012                 nodes.push(ns[i]);
19013             }
19014         } else{
19015             for(var i = start; i >= end; i--){
19016                 nodes.push(ns[i]);
19017             }
19018         }
19019         return nodes;
19020     },
19021
19022     /**
19023      * Finds the index of the passed node
19024      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025      * @return {Number} The index of the node or -1
19026      */
19027     indexOf : function(node){
19028         node = this.getNode(node);
19029         if(typeof node.nodeIndex == "number"){
19030             return node.nodeIndex;
19031         }
19032         var ns = this.nodes;
19033         for(var i = 0, len = ns.length; i < len; i++){
19034             if(ns[i] == node){
19035                 return i;
19036             }
19037         }
19038         return -1;
19039     }
19040 });
19041 /*
19042  * - LGPL
19043  *
19044  * based on jquery fullcalendar
19045  * 
19046  */
19047
19048 Roo.bootstrap = Roo.bootstrap || {};
19049 /**
19050  * @class Roo.bootstrap.Calendar
19051  * @extends Roo.bootstrap.Component
19052  * Bootstrap Calendar class
19053  * @cfg {Boolean} loadMask (true|false) default false
19054  * @cfg {Object} header generate the user specific header of the calendar, default false
19055
19056  * @constructor
19057  * Create a new Container
19058  * @param {Object} config The config object
19059  */
19060
19061
19062
19063 Roo.bootstrap.Calendar = function(config){
19064     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19065      this.addEvents({
19066         /**
19067              * @event select
19068              * Fires when a date is selected
19069              * @param {DatePicker} this
19070              * @param {Date} date The selected date
19071              */
19072         'select': true,
19073         /**
19074              * @event monthchange
19075              * Fires when the displayed month changes 
19076              * @param {DatePicker} this
19077              * @param {Date} date The selected month
19078              */
19079         'monthchange': true,
19080         /**
19081              * @event evententer
19082              * Fires when mouse over an event
19083              * @param {Calendar} this
19084              * @param {event} Event
19085              */
19086         'evententer': true,
19087         /**
19088              * @event eventleave
19089              * Fires when the mouse leaves an
19090              * @param {Calendar} this
19091              * @param {event}
19092              */
19093         'eventleave': true,
19094         /**
19095              * @event eventclick
19096              * Fires when the mouse click an
19097              * @param {Calendar} this
19098              * @param {event}
19099              */
19100         'eventclick': true
19101         
19102     });
19103
19104 };
19105
19106 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19107     
19108      /**
19109      * @cfg {Number} startDay
19110      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19111      */
19112     startDay : 0,
19113     
19114     loadMask : false,
19115     
19116     header : false,
19117       
19118     getAutoCreate : function(){
19119         
19120         
19121         var fc_button = function(name, corner, style, content ) {
19122             return Roo.apply({},{
19123                 tag : 'span',
19124                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19125                          (corner.length ?
19126                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19127                             ''
19128                         ),
19129                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19130                 unselectable: 'on'
19131             });
19132         };
19133         
19134         var header = {};
19135         
19136         if(!this.header){
19137             header = {
19138                 tag : 'table',
19139                 cls : 'fc-header',
19140                 style : 'width:100%',
19141                 cn : [
19142                     {
19143                         tag: 'tr',
19144                         cn : [
19145                             {
19146                                 tag : 'td',
19147                                 cls : 'fc-header-left',
19148                                 cn : [
19149                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19150                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19151                                     { tag: 'span', cls: 'fc-header-space' },
19152                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19153
19154
19155                                 ]
19156                             },
19157
19158                             {
19159                                 tag : 'td',
19160                                 cls : 'fc-header-center',
19161                                 cn : [
19162                                     {
19163                                         tag: 'span',
19164                                         cls: 'fc-header-title',
19165                                         cn : {
19166                                             tag: 'H2',
19167                                             html : 'month / year'
19168                                         }
19169                                     }
19170
19171                                 ]
19172                             },
19173                             {
19174                                 tag : 'td',
19175                                 cls : 'fc-header-right',
19176                                 cn : [
19177                               /*      fc_button('month', 'left', '', 'month' ),
19178                                     fc_button('week', '', '', 'week' ),
19179                                     fc_button('day', 'right', '', 'day' )
19180                                 */    
19181
19182                                 ]
19183                             }
19184
19185                         ]
19186                     }
19187                 ]
19188             };
19189         }
19190         
19191         header = this.header;
19192         
19193        
19194         var cal_heads = function() {
19195             var ret = [];
19196             // fixme - handle this.
19197             
19198             for (var i =0; i < Date.dayNames.length; i++) {
19199                 var d = Date.dayNames[i];
19200                 ret.push({
19201                     tag: 'th',
19202                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19203                     html : d.substring(0,3)
19204                 });
19205                 
19206             }
19207             ret[0].cls += ' fc-first';
19208             ret[6].cls += ' fc-last';
19209             return ret;
19210         };
19211         var cal_cell = function(n) {
19212             return  {
19213                 tag: 'td',
19214                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19215                 cn : [
19216                     {
19217                         cn : [
19218                             {
19219                                 cls: 'fc-day-number',
19220                                 html: 'D'
19221                             },
19222                             {
19223                                 cls: 'fc-day-content',
19224                              
19225                                 cn : [
19226                                      {
19227                                         style: 'position: relative;' // height: 17px;
19228                                     }
19229                                 ]
19230                             }
19231                             
19232                             
19233                         ]
19234                     }
19235                 ]
19236                 
19237             }
19238         };
19239         var cal_rows = function() {
19240             
19241             var ret = [];
19242             for (var r = 0; r < 6; r++) {
19243                 var row= {
19244                     tag : 'tr',
19245                     cls : 'fc-week',
19246                     cn : []
19247                 };
19248                 
19249                 for (var i =0; i < Date.dayNames.length; i++) {
19250                     var d = Date.dayNames[i];
19251                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19252
19253                 }
19254                 row.cn[0].cls+=' fc-first';
19255                 row.cn[0].cn[0].style = 'min-height:90px';
19256                 row.cn[6].cls+=' fc-last';
19257                 ret.push(row);
19258                 
19259             }
19260             ret[0].cls += ' fc-first';
19261             ret[4].cls += ' fc-prev-last';
19262             ret[5].cls += ' fc-last';
19263             return ret;
19264             
19265         };
19266         
19267         var cal_table = {
19268             tag: 'table',
19269             cls: 'fc-border-separate',
19270             style : 'width:100%',
19271             cellspacing  : 0,
19272             cn : [
19273                 { 
19274                     tag: 'thead',
19275                     cn : [
19276                         { 
19277                             tag: 'tr',
19278                             cls : 'fc-first fc-last',
19279                             cn : cal_heads()
19280                         }
19281                     ]
19282                 },
19283                 { 
19284                     tag: 'tbody',
19285                     cn : cal_rows()
19286                 }
19287                   
19288             ]
19289         };
19290          
19291          var cfg = {
19292             cls : 'fc fc-ltr',
19293             cn : [
19294                 header,
19295                 {
19296                     cls : 'fc-content',
19297                     style : "position: relative;",
19298                     cn : [
19299                         {
19300                             cls : 'fc-view fc-view-month fc-grid',
19301                             style : 'position: relative',
19302                             unselectable : 'on',
19303                             cn : [
19304                                 {
19305                                     cls : 'fc-event-container',
19306                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19307                                 },
19308                                 cal_table
19309                             ]
19310                         }
19311                     ]
19312     
19313                 }
19314            ] 
19315             
19316         };
19317         
19318          
19319         
19320         return cfg;
19321     },
19322     
19323     
19324     initEvents : function()
19325     {
19326         if(!this.store){
19327             throw "can not find store for calendar";
19328         }
19329         
19330         var mark = {
19331             tag: "div",
19332             cls:"x-dlg-mask",
19333             style: "text-align:center",
19334             cn: [
19335                 {
19336                     tag: "div",
19337                     style: "background-color:white;width:50%;margin:250 auto",
19338                     cn: [
19339                         {
19340                             tag: "img",
19341                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19342                         },
19343                         {
19344                             tag: "span",
19345                             html: "Loading"
19346                         }
19347                         
19348                     ]
19349                 }
19350             ]
19351         };
19352         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19353         
19354         var size = this.el.select('.fc-content', true).first().getSize();
19355         this.maskEl.setSize(size.width, size.height);
19356         this.maskEl.enableDisplayMode("block");
19357         if(!this.loadMask){
19358             this.maskEl.hide();
19359         }
19360         
19361         this.store = Roo.factory(this.store, Roo.data);
19362         this.store.on('load', this.onLoad, this);
19363         this.store.on('beforeload', this.onBeforeLoad, this);
19364         
19365         this.resize();
19366         
19367         this.cells = this.el.select('.fc-day',true);
19368         //Roo.log(this.cells);
19369         this.textNodes = this.el.query('.fc-day-number');
19370         this.cells.addClassOnOver('fc-state-hover');
19371         
19372         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19373         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19374         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19375         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19376         
19377         this.on('monthchange', this.onMonthChange, this);
19378         
19379         this.update(new Date().clearTime());
19380     },
19381     
19382     resize : function() {
19383         var sz  = this.el.getSize();
19384         
19385         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19386         this.el.select('.fc-day-content div',true).setHeight(34);
19387     },
19388     
19389     
19390     // private
19391     showPrevMonth : function(e){
19392         this.update(this.activeDate.add("mo", -1));
19393     },
19394     showToday : function(e){
19395         this.update(new Date().clearTime());
19396     },
19397     // private
19398     showNextMonth : function(e){
19399         this.update(this.activeDate.add("mo", 1));
19400     },
19401
19402     // private
19403     showPrevYear : function(){
19404         this.update(this.activeDate.add("y", -1));
19405     },
19406
19407     // private
19408     showNextYear : function(){
19409         this.update(this.activeDate.add("y", 1));
19410     },
19411
19412     
19413    // private
19414     update : function(date)
19415     {
19416         var vd = this.activeDate;
19417         this.activeDate = date;
19418 //        if(vd && this.el){
19419 //            var t = date.getTime();
19420 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19421 //                Roo.log('using add remove');
19422 //                
19423 //                this.fireEvent('monthchange', this, date);
19424 //                
19425 //                this.cells.removeClass("fc-state-highlight");
19426 //                this.cells.each(function(c){
19427 //                   if(c.dateValue == t){
19428 //                       c.addClass("fc-state-highlight");
19429 //                       setTimeout(function(){
19430 //                            try{c.dom.firstChild.focus();}catch(e){}
19431 //                       }, 50);
19432 //                       return false;
19433 //                   }
19434 //                   return true;
19435 //                });
19436 //                return;
19437 //            }
19438 //        }
19439         
19440         var days = date.getDaysInMonth();
19441         
19442         var firstOfMonth = date.getFirstDateOfMonth();
19443         var startingPos = firstOfMonth.getDay()-this.startDay;
19444         
19445         if(startingPos < this.startDay){
19446             startingPos += 7;
19447         }
19448         
19449         var pm = date.add(Date.MONTH, -1);
19450         var prevStart = pm.getDaysInMonth()-startingPos;
19451 //        
19452         this.cells = this.el.select('.fc-day',true);
19453         this.textNodes = this.el.query('.fc-day-number');
19454         this.cells.addClassOnOver('fc-state-hover');
19455         
19456         var cells = this.cells.elements;
19457         var textEls = this.textNodes;
19458         
19459         Roo.each(cells, function(cell){
19460             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19461         });
19462         
19463         days += startingPos;
19464
19465         // convert everything to numbers so it's fast
19466         var day = 86400000;
19467         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19468         //Roo.log(d);
19469         //Roo.log(pm);
19470         //Roo.log(prevStart);
19471         
19472         var today = new Date().clearTime().getTime();
19473         var sel = date.clearTime().getTime();
19474         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19475         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19476         var ddMatch = this.disabledDatesRE;
19477         var ddText = this.disabledDatesText;
19478         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19479         var ddaysText = this.disabledDaysText;
19480         var format = this.format;
19481         
19482         var setCellClass = function(cal, cell){
19483             cell.row = 0;
19484             cell.events = [];
19485             cell.more = [];
19486             //Roo.log('set Cell Class');
19487             cell.title = "";
19488             var t = d.getTime();
19489             
19490             //Roo.log(d);
19491             
19492             cell.dateValue = t;
19493             if(t == today){
19494                 cell.className += " fc-today";
19495                 cell.className += " fc-state-highlight";
19496                 cell.title = cal.todayText;
19497             }
19498             if(t == sel){
19499                 // disable highlight in other month..
19500                 //cell.className += " fc-state-highlight";
19501                 
19502             }
19503             // disabling
19504             if(t < min) {
19505                 cell.className = " fc-state-disabled";
19506                 cell.title = cal.minText;
19507                 return;
19508             }
19509             if(t > max) {
19510                 cell.className = " fc-state-disabled";
19511                 cell.title = cal.maxText;
19512                 return;
19513             }
19514             if(ddays){
19515                 if(ddays.indexOf(d.getDay()) != -1){
19516                     cell.title = ddaysText;
19517                     cell.className = " fc-state-disabled";
19518                 }
19519             }
19520             if(ddMatch && format){
19521                 var fvalue = d.dateFormat(format);
19522                 if(ddMatch.test(fvalue)){
19523                     cell.title = ddText.replace("%0", fvalue);
19524                     cell.className = " fc-state-disabled";
19525                 }
19526             }
19527             
19528             if (!cell.initialClassName) {
19529                 cell.initialClassName = cell.dom.className;
19530             }
19531             
19532             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19533         };
19534
19535         var i = 0;
19536         
19537         for(; i < startingPos; i++) {
19538             textEls[i].innerHTML = (++prevStart);
19539             d.setDate(d.getDate()+1);
19540             
19541             cells[i].className = "fc-past fc-other-month";
19542             setCellClass(this, cells[i]);
19543         }
19544         
19545         var intDay = 0;
19546         
19547         for(; i < days; i++){
19548             intDay = i - startingPos + 1;
19549             textEls[i].innerHTML = (intDay);
19550             d.setDate(d.getDate()+1);
19551             
19552             cells[i].className = ''; // "x-date-active";
19553             setCellClass(this, cells[i]);
19554         }
19555         var extraDays = 0;
19556         
19557         for(; i < 42; i++) {
19558             textEls[i].innerHTML = (++extraDays);
19559             d.setDate(d.getDate()+1);
19560             
19561             cells[i].className = "fc-future fc-other-month";
19562             setCellClass(this, cells[i]);
19563         }
19564         
19565         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19566         
19567         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19568         
19569         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19570         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19571         
19572         if(totalRows != 6){
19573             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19574             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19575         }
19576         
19577         this.fireEvent('monthchange', this, date);
19578         
19579         
19580         /*
19581         if(!this.internalRender){
19582             var main = this.el.dom.firstChild;
19583             var w = main.offsetWidth;
19584             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19585             Roo.fly(main).setWidth(w);
19586             this.internalRender = true;
19587             // opera does not respect the auto grow header center column
19588             // then, after it gets a width opera refuses to recalculate
19589             // without a second pass
19590             if(Roo.isOpera && !this.secondPass){
19591                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19592                 this.secondPass = true;
19593                 this.update.defer(10, this, [date]);
19594             }
19595         }
19596         */
19597         
19598     },
19599     
19600     findCell : function(dt) {
19601         dt = dt.clearTime().getTime();
19602         var ret = false;
19603         this.cells.each(function(c){
19604             //Roo.log("check " +c.dateValue + '?=' + dt);
19605             if(c.dateValue == dt){
19606                 ret = c;
19607                 return false;
19608             }
19609             return true;
19610         });
19611         
19612         return ret;
19613     },
19614     
19615     findCells : function(ev) {
19616         var s = ev.start.clone().clearTime().getTime();
19617        // Roo.log(s);
19618         var e= ev.end.clone().clearTime().getTime();
19619        // Roo.log(e);
19620         var ret = [];
19621         this.cells.each(function(c){
19622              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19623             
19624             if(c.dateValue > e){
19625                 return ;
19626             }
19627             if(c.dateValue < s){
19628                 return ;
19629             }
19630             ret.push(c);
19631         });
19632         
19633         return ret;    
19634     },
19635     
19636 //    findBestRow: function(cells)
19637 //    {
19638 //        var ret = 0;
19639 //        
19640 //        for (var i =0 ; i < cells.length;i++) {
19641 //            ret  = Math.max(cells[i].rows || 0,ret);
19642 //        }
19643 //        return ret;
19644 //        
19645 //    },
19646     
19647     
19648     addItem : function(ev)
19649     {
19650         // look for vertical location slot in
19651         var cells = this.findCells(ev);
19652         
19653 //        ev.row = this.findBestRow(cells);
19654         
19655         // work out the location.
19656         
19657         var crow = false;
19658         var rows = [];
19659         for(var i =0; i < cells.length; i++) {
19660             
19661             cells[i].row = cells[0].row;
19662             
19663             if(i == 0){
19664                 cells[i].row = cells[i].row + 1;
19665             }
19666             
19667             if (!crow) {
19668                 crow = {
19669                     start : cells[i],
19670                     end :  cells[i]
19671                 };
19672                 continue;
19673             }
19674             if (crow.start.getY() == cells[i].getY()) {
19675                 // on same row.
19676                 crow.end = cells[i];
19677                 continue;
19678             }
19679             // different row.
19680             rows.push(crow);
19681             crow = {
19682                 start: cells[i],
19683                 end : cells[i]
19684             };
19685             
19686         }
19687         
19688         rows.push(crow);
19689         ev.els = [];
19690         ev.rows = rows;
19691         ev.cells = cells;
19692         
19693         cells[0].events.push(ev);
19694         
19695         this.calevents.push(ev);
19696     },
19697     
19698     clearEvents: function() {
19699         
19700         if(!this.calevents){
19701             return;
19702         }
19703         
19704         Roo.each(this.cells.elements, function(c){
19705             c.row = 0;
19706             c.events = [];
19707             c.more = [];
19708         });
19709         
19710         Roo.each(this.calevents, function(e) {
19711             Roo.each(e.els, function(el) {
19712                 el.un('mouseenter' ,this.onEventEnter, this);
19713                 el.un('mouseleave' ,this.onEventLeave, this);
19714                 el.remove();
19715             },this);
19716         },this);
19717         
19718         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19719             e.remove();
19720         });
19721         
19722     },
19723     
19724     renderEvents: function()
19725     {   
19726         var _this = this;
19727         
19728         this.cells.each(function(c) {
19729             
19730             if(c.row < 5){
19731                 return;
19732             }
19733             
19734             var ev = c.events;
19735             
19736             var r = 4;
19737             if(c.row != c.events.length){
19738                 r = 4 - (4 - (c.row - c.events.length));
19739             }
19740             
19741             c.events = ev.slice(0, r);
19742             c.more = ev.slice(r);
19743             
19744             if(c.more.length && c.more.length == 1){
19745                 c.events.push(c.more.pop());
19746             }
19747             
19748             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19749             
19750         });
19751             
19752         this.cells.each(function(c) {
19753             
19754             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19755             
19756             
19757             for (var e = 0; e < c.events.length; e++){
19758                 var ev = c.events[e];
19759                 var rows = ev.rows;
19760                 
19761                 for(var i = 0; i < rows.length; i++) {
19762                 
19763                     // how many rows should it span..
19764
19765                     var  cfg = {
19766                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19767                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19768
19769                         unselectable : "on",
19770                         cn : [
19771                             {
19772                                 cls: 'fc-event-inner',
19773                                 cn : [
19774     //                                {
19775     //                                  tag:'span',
19776     //                                  cls: 'fc-event-time',
19777     //                                  html : cells.length > 1 ? '' : ev.time
19778     //                                },
19779                                     {
19780                                       tag:'span',
19781                                       cls: 'fc-event-title',
19782                                       html : String.format('{0}', ev.title)
19783                                     }
19784
19785
19786                                 ]
19787                             },
19788                             {
19789                                 cls: 'ui-resizable-handle ui-resizable-e',
19790                                 html : '&nbsp;&nbsp;&nbsp'
19791                             }
19792
19793                         ]
19794                     };
19795
19796                     if (i == 0) {
19797                         cfg.cls += ' fc-event-start';
19798                     }
19799                     if ((i+1) == rows.length) {
19800                         cfg.cls += ' fc-event-end';
19801                     }
19802
19803                     var ctr = _this.el.select('.fc-event-container',true).first();
19804                     var cg = ctr.createChild(cfg);
19805
19806                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19807                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19808
19809                     var r = (c.more.length) ? 1 : 0;
19810                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19811                     cg.setWidth(ebox.right - sbox.x -2);
19812
19813                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19814                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19815                     cg.on('click', _this.onEventClick, _this, ev);
19816
19817                     ev.els.push(cg);
19818                     
19819                 }
19820                 
19821             }
19822             
19823             
19824             if(c.more.length){
19825                 var  cfg = {
19826                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19827                     style : 'position: absolute',
19828                     unselectable : "on",
19829                     cn : [
19830                         {
19831                             cls: 'fc-event-inner',
19832                             cn : [
19833                                 {
19834                                   tag:'span',
19835                                   cls: 'fc-event-title',
19836                                   html : 'More'
19837                                 }
19838
19839
19840                             ]
19841                         },
19842                         {
19843                             cls: 'ui-resizable-handle ui-resizable-e',
19844                             html : '&nbsp;&nbsp;&nbsp'
19845                         }
19846
19847                     ]
19848                 };
19849
19850                 var ctr = _this.el.select('.fc-event-container',true).first();
19851                 var cg = ctr.createChild(cfg);
19852
19853                 var sbox = c.select('.fc-day-content',true).first().getBox();
19854                 var ebox = c.select('.fc-day-content',true).first().getBox();
19855                 //Roo.log(cg);
19856                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19857                 cg.setWidth(ebox.right - sbox.x -2);
19858
19859                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19860                 
19861             }
19862             
19863         });
19864         
19865         
19866         
19867     },
19868     
19869     onEventEnter: function (e, el,event,d) {
19870         this.fireEvent('evententer', this, el, event);
19871     },
19872     
19873     onEventLeave: function (e, el,event,d) {
19874         this.fireEvent('eventleave', this, el, event);
19875     },
19876     
19877     onEventClick: function (e, el,event,d) {
19878         this.fireEvent('eventclick', this, el, event);
19879     },
19880     
19881     onMonthChange: function () {
19882         this.store.load();
19883     },
19884     
19885     onMoreEventClick: function(e, el, more)
19886     {
19887         var _this = this;
19888         
19889         this.calpopover.placement = 'right';
19890         this.calpopover.setTitle('More');
19891         
19892         this.calpopover.setContent('');
19893         
19894         var ctr = this.calpopover.el.select('.popover-content', true).first();
19895         
19896         Roo.each(more, function(m){
19897             var cfg = {
19898                 cls : 'fc-event-hori fc-event-draggable',
19899                 html : m.title
19900             };
19901             var cg = ctr.createChild(cfg);
19902             
19903             cg.on('click', _this.onEventClick, _this, m);
19904         });
19905         
19906         this.calpopover.show(el);
19907         
19908         
19909     },
19910     
19911     onLoad: function () 
19912     {   
19913         this.calevents = [];
19914         var cal = this;
19915         
19916         if(this.store.getCount() > 0){
19917             this.store.data.each(function(d){
19918                cal.addItem({
19919                     id : d.data.id,
19920                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19921                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19922                     time : d.data.start_time,
19923                     title : d.data.title,
19924                     description : d.data.description,
19925                     venue : d.data.venue
19926                 });
19927             });
19928         }
19929         
19930         this.renderEvents();
19931         
19932         if(this.calevents.length && this.loadMask){
19933             this.maskEl.hide();
19934         }
19935     },
19936     
19937     onBeforeLoad: function()
19938     {
19939         this.clearEvents();
19940         if(this.loadMask){
19941             this.maskEl.show();
19942         }
19943     }
19944 });
19945
19946  
19947  /*
19948  * - LGPL
19949  *
19950  * element
19951  * 
19952  */
19953
19954 /**
19955  * @class Roo.bootstrap.Popover
19956  * @extends Roo.bootstrap.Component
19957  * Bootstrap Popover class
19958  * @cfg {String} html contents of the popover   (or false to use children..)
19959  * @cfg {String} title of popover (or false to hide)
19960  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19961  * @cfg {String} trigger click || hover (or false to trigger manually)
19962  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19963  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19964  *      - if false and it has a 'parent' then it will be automatically added to that element
19965  *      - if string - Roo.get  will be called 
19966  * @cfg {Number} delay - delay before showing
19967  
19968  * @constructor
19969  * Create a new Popover
19970  * @param {Object} config The config object
19971  */
19972
19973 Roo.bootstrap.Popover = function(config){
19974     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19975     
19976     this.addEvents({
19977         // raw events
19978          /**
19979          * @event show
19980          * After the popover show
19981          * 
19982          * @param {Roo.bootstrap.Popover} this
19983          */
19984         "show" : true,
19985         /**
19986          * @event hide
19987          * After the popover hide
19988          * 
19989          * @param {Roo.bootstrap.Popover} this
19990          */
19991         "hide" : true
19992     });
19993 };
19994
19995 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19996     
19997     title: false,
19998     html: false,
19999     
20000     placement : 'right',
20001     trigger : 'hover', // hover
20002     modal : false,
20003     delay : 0,
20004     
20005     over: false,
20006     
20007     can_build_overlaid : false,
20008     
20009     maskEl : false, // the mask element
20010     headerEl : false,
20011     contentEl : false,
20012     alignEl : false, // when show is called with an element - this get's stored.
20013     
20014     getChildContainer : function()
20015     {
20016         return this.contentEl;
20017         
20018     },
20019     getPopoverHeader : function()
20020     {
20021         this.title = true; // flag not to hide it..
20022         this.headerEl.addClass('p-0');
20023         return this.headerEl
20024     },
20025     
20026     
20027     getAutoCreate : function(){
20028          
20029         var cfg = {
20030            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20031            style: 'display:block',
20032            cn : [
20033                 {
20034                     cls : 'arrow'
20035                 },
20036                 {
20037                     cls : 'popover-inner ',
20038                     cn : [
20039                         {
20040                             tag: 'h3',
20041                             cls: 'popover-title popover-header',
20042                             html : this.title === false ? '' : this.title
20043                         },
20044                         {
20045                             cls : 'popover-content popover-body '  + (this.cls || ''),
20046                             html : this.html || ''
20047                         }
20048                     ]
20049                     
20050                 }
20051            ]
20052         };
20053         
20054         return cfg;
20055     },
20056     /**
20057      * @param {string} the title
20058      */
20059     setTitle: function(str)
20060     {
20061         this.title = str;
20062         if (this.el) {
20063             this.headerEl.dom.innerHTML = str;
20064         }
20065         
20066     },
20067     /**
20068      * @param {string} the body content
20069      */
20070     setContent: function(str)
20071     {
20072         this.html = str;
20073         if (this.contentEl) {
20074             this.contentEl.dom.innerHTML = str;
20075         }
20076         
20077     },
20078     // as it get's added to the bottom of the page.
20079     onRender : function(ct, position)
20080     {
20081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20082         
20083         
20084         
20085         if(!this.el){
20086             var cfg = Roo.apply({},  this.getAutoCreate());
20087             cfg.id = Roo.id();
20088             
20089             if (this.cls) {
20090                 cfg.cls += ' ' + this.cls;
20091             }
20092             if (this.style) {
20093                 cfg.style = this.style;
20094             }
20095             //Roo.log("adding to ");
20096             this.el = Roo.get(document.body).createChild(cfg, position);
20097 //            Roo.log(this.el);
20098         }
20099         
20100         this.contentEl = this.el.select('.popover-content',true).first();
20101         this.headerEl =  this.el.select('.popover-title',true).first();
20102         
20103         var nitems = [];
20104         if(typeof(this.items) != 'undefined'){
20105             var items = this.items;
20106             delete this.items;
20107
20108             for(var i =0;i < items.length;i++) {
20109                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20110             }
20111         }
20112
20113         this.items = nitems;
20114         
20115         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20116         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20117         
20118         
20119         
20120         this.initEvents();
20121     },
20122     
20123     resizeMask : function()
20124     {
20125         this.maskEl.setSize(
20126             Roo.lib.Dom.getViewWidth(true),
20127             Roo.lib.Dom.getViewHeight(true)
20128         );
20129     },
20130     
20131     initEvents : function()
20132     {
20133         
20134         if (!this.modal) { 
20135             Roo.bootstrap.Popover.register(this);
20136         }
20137          
20138         this.arrowEl = this.el.select('.arrow',true).first();
20139         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20140         this.el.enableDisplayMode('block');
20141         this.el.hide();
20142  
20143         
20144         if (this.over === false && !this.parent()) {
20145             return; 
20146         }
20147         if (this.triggers === false) {
20148             return;
20149         }
20150          
20151         // support parent
20152         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20153         var triggers = this.trigger ? this.trigger.split(' ') : [];
20154         Roo.each(triggers, function(trigger) {
20155         
20156             if (trigger == 'click') {
20157                 on_el.on('click', this.toggle, this);
20158             } else if (trigger != 'manual') {
20159                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20160                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20161       
20162                 on_el.on(eventIn  ,this.enter, this);
20163                 on_el.on(eventOut, this.leave, this);
20164             }
20165         }, this);
20166     },
20167     
20168     
20169     // private
20170     timeout : null,
20171     hoverState : null,
20172     
20173     toggle : function () {
20174         this.hoverState == 'in' ? this.leave() : this.enter();
20175     },
20176     
20177     enter : function () {
20178         
20179         clearTimeout(this.timeout);
20180     
20181         this.hoverState = 'in';
20182     
20183         if (!this.delay || !this.delay.show) {
20184             this.show();
20185             return;
20186         }
20187         var _t = this;
20188         this.timeout = setTimeout(function () {
20189             if (_t.hoverState == 'in') {
20190                 _t.show();
20191             }
20192         }, this.delay.show)
20193     },
20194     
20195     leave : function() {
20196         clearTimeout(this.timeout);
20197     
20198         this.hoverState = 'out';
20199     
20200         if (!this.delay || !this.delay.hide) {
20201             this.hide();
20202             return;
20203         }
20204         var _t = this;
20205         this.timeout = setTimeout(function () {
20206             if (_t.hoverState == 'out') {
20207                 _t.hide();
20208             }
20209         }, this.delay.hide)
20210     },
20211     /**
20212      * Show the popover
20213      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20214      * @param {string} (left|right|top|bottom) position
20215      */
20216     show : function (on_el, placement)
20217     {
20218         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20219         on_el = on_el || false; // default to false
20220          
20221         if (!on_el) {
20222             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20223                 on_el = this.parent().el;
20224             } else if (this.over) {
20225                 on_el = Roo.get(this.over);
20226             }
20227             
20228         }
20229         
20230         this.alignEl = Roo.get( on_el );
20231
20232         if (!this.el) {
20233             this.render(document.body);
20234         }
20235         
20236         
20237          
20238         
20239         if (this.title === false) {
20240             this.headerEl.hide();
20241         }
20242         
20243        
20244         this.el.show();
20245         this.el.dom.style.display = 'block';
20246          
20247  
20248         if (this.alignEl) {
20249             this.updatePosition(this.placement, true);
20250              
20251         } else {
20252             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20253             var es = this.el.getSize();
20254             var x = Roo.lib.Dom.getViewWidth()/2;
20255             var y = Roo.lib.Dom.getViewHeight()/2;
20256             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20257             
20258         }
20259
20260         
20261         //var arrow = this.el.select('.arrow',true).first();
20262         //arrow.set(align[2], 
20263         
20264         this.el.addClass('in');
20265         
20266          
20267         
20268         this.hoverState = 'in';
20269         
20270         if (this.modal) {
20271             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20272             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20273             this.maskEl.dom.style.display = 'block';
20274             this.maskEl.addClass('show');
20275         }
20276         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20277  
20278         this.fireEvent('show', this);
20279         
20280     },
20281     /**
20282      * fire this manually after loading a grid in the table for example
20283      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20284      * @param {Boolean} try and move it if we cant get right position.
20285      */
20286     updatePosition : function(placement, try_move)
20287     {
20288         // allow for calling with no parameters
20289         placement = placement   ? placement :  this.placement;
20290         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20291         
20292         this.el.removeClass([
20293             'fade','top','bottom', 'left', 'right','in',
20294             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20295         ]);
20296         this.el.addClass(placement + ' bs-popover-' + placement);
20297         
20298         if (!this.alignEl ) {
20299             return false;
20300         }
20301         
20302         switch (placement) {
20303             case 'right':
20304                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20305                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20306                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20307                     //normal display... or moved up/down.
20308                     this.el.setXY(offset);
20309                     var xy = this.alignEl.getAnchorXY('tr', false);
20310                     xy[0]+=2;xy[1]+=5;
20311                     this.arrowEl.setXY(xy);
20312                     return true;
20313                 }
20314                 // continue through...
20315                 return this.updatePosition('left', false);
20316                 
20317             
20318             case 'left':
20319                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20320                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20321                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20322                     //normal display... or moved up/down.
20323                     this.el.setXY(offset);
20324                     var xy = this.alignEl.getAnchorXY('tl', false);
20325                     xy[0]-=10;xy[1]+=5; // << fix me
20326                     this.arrowEl.setXY(xy);
20327                     return true;
20328                 }
20329                 // call self...
20330                 return this.updatePosition('right', false);
20331             
20332             case 'top':
20333                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20334                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20335                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20336                     //normal display... or moved up/down.
20337                     this.el.setXY(offset);
20338                     var xy = this.alignEl.getAnchorXY('t', false);
20339                     xy[1]-=10; // << fix me
20340                     this.arrowEl.setXY(xy);
20341                     return true;
20342                 }
20343                 // fall through
20344                return this.updatePosition('bottom', false);
20345             
20346             case 'bottom':
20347                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20348                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20349                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20350                     //normal display... or moved up/down.
20351                     this.el.setXY(offset);
20352                     var xy = this.alignEl.getAnchorXY('b', false);
20353                      xy[1]+=2; // << fix me
20354                     this.arrowEl.setXY(xy);
20355                     return true;
20356                 }
20357                 // fall through
20358                 return this.updatePosition('top', false);
20359                 
20360             
20361         }
20362         
20363         
20364         return false;
20365     },
20366     
20367     hide : function()
20368     {
20369         this.el.setXY([0,0]);
20370         this.el.removeClass('in');
20371         this.el.hide();
20372         this.hoverState = null;
20373         this.maskEl.hide(); // always..
20374         this.fireEvent('hide', this);
20375     }
20376     
20377 });
20378
20379
20380 Roo.apply(Roo.bootstrap.Popover, {
20381
20382     alignment : {
20383         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20384         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20385         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20386         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20387     },
20388     
20389     zIndex : 20001,
20390
20391     clickHander : false,
20392     
20393
20394     onMouseDown : function(e)
20395     {
20396         if (!e.getTarget(".roo-popover")) {
20397             this.hideAll();
20398         }
20399          
20400     },
20401     
20402     popups : [],
20403     
20404     register : function(popup)
20405     {
20406         if (!Roo.bootstrap.Popover.clickHandler) {
20407             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20408         }
20409         // hide other popups.
20410         this.hideAll();
20411         this.popups.push(popup);
20412     },
20413     hideAll : function()
20414     {
20415         this.popups.forEach(function(p) {
20416             p.hide();
20417         });
20418     }
20419
20420 });/*
20421  * - LGPL
20422  *
20423  * Card header - holder for the card header elements.
20424  * 
20425  */
20426
20427 /**
20428  * @class Roo.bootstrap.PopoverNav
20429  * @extends Roo.bootstrap.NavGroup
20430  * Bootstrap Popover header navigation class
20431  * @constructor
20432  * Create a new Popover Header Navigation 
20433  * @param {Object} config The config object
20434  */
20435
20436 Roo.bootstrap.PopoverNav = function(config){
20437     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20438 };
20439
20440 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20441     
20442     
20443     container_method : 'getPopoverHeader' 
20444     
20445      
20446     
20447     
20448    
20449 });
20450
20451  
20452
20453  /*
20454  * - LGPL
20455  *
20456  * Progress
20457  * 
20458  */
20459
20460 /**
20461  * @class Roo.bootstrap.Progress
20462  * @extends Roo.bootstrap.Component
20463  * Bootstrap Progress class
20464  * @cfg {Boolean} striped striped of the progress bar
20465  * @cfg {Boolean} active animated of the progress bar
20466  * 
20467  * 
20468  * @constructor
20469  * Create a new Progress
20470  * @param {Object} config The config object
20471  */
20472
20473 Roo.bootstrap.Progress = function(config){
20474     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20475 };
20476
20477 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20478     
20479     striped : false,
20480     active: false,
20481     
20482     getAutoCreate : function(){
20483         var cfg = {
20484             tag: 'div',
20485             cls: 'progress'
20486         };
20487         
20488         
20489         if(this.striped){
20490             cfg.cls += ' progress-striped';
20491         }
20492       
20493         if(this.active){
20494             cfg.cls += ' active';
20495         }
20496         
20497         
20498         return cfg;
20499     }
20500    
20501 });
20502
20503  
20504
20505  /*
20506  * - LGPL
20507  *
20508  * ProgressBar
20509  * 
20510  */
20511
20512 /**
20513  * @class Roo.bootstrap.ProgressBar
20514  * @extends Roo.bootstrap.Component
20515  * Bootstrap ProgressBar class
20516  * @cfg {Number} aria_valuenow aria-value now
20517  * @cfg {Number} aria_valuemin aria-value min
20518  * @cfg {Number} aria_valuemax aria-value max
20519  * @cfg {String} label label for the progress bar
20520  * @cfg {String} panel (success | info | warning | danger )
20521  * @cfg {String} role role of the progress bar
20522  * @cfg {String} sr_only text
20523  * 
20524  * 
20525  * @constructor
20526  * Create a new ProgressBar
20527  * @param {Object} config The config object
20528  */
20529
20530 Roo.bootstrap.ProgressBar = function(config){
20531     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20532 };
20533
20534 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20535     
20536     aria_valuenow : 0,
20537     aria_valuemin : 0,
20538     aria_valuemax : 100,
20539     label : false,
20540     panel : false,
20541     role : false,
20542     sr_only: false,
20543     
20544     getAutoCreate : function()
20545     {
20546         
20547         var cfg = {
20548             tag: 'div',
20549             cls: 'progress-bar',
20550             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20551         };
20552         
20553         if(this.sr_only){
20554             cfg.cn = {
20555                 tag: 'span',
20556                 cls: 'sr-only',
20557                 html: this.sr_only
20558             }
20559         }
20560         
20561         if(this.role){
20562             cfg.role = this.role;
20563         }
20564         
20565         if(this.aria_valuenow){
20566             cfg['aria-valuenow'] = this.aria_valuenow;
20567         }
20568         
20569         if(this.aria_valuemin){
20570             cfg['aria-valuemin'] = this.aria_valuemin;
20571         }
20572         
20573         if(this.aria_valuemax){
20574             cfg['aria-valuemax'] = this.aria_valuemax;
20575         }
20576         
20577         if(this.label && !this.sr_only){
20578             cfg.html = this.label;
20579         }
20580         
20581         if(this.panel){
20582             cfg.cls += ' progress-bar-' + this.panel;
20583         }
20584         
20585         return cfg;
20586     },
20587     
20588     update : function(aria_valuenow)
20589     {
20590         this.aria_valuenow = aria_valuenow;
20591         
20592         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20593     }
20594    
20595 });
20596
20597  
20598
20599  /*
20600  * - LGPL
20601  *
20602  * column
20603  * 
20604  */
20605
20606 /**
20607  * @class Roo.bootstrap.TabGroup
20608  * @extends Roo.bootstrap.Column
20609  * Bootstrap Column class
20610  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20611  * @cfg {Boolean} carousel true to make the group behave like a carousel
20612  * @cfg {Boolean} bullets show bullets for the panels
20613  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20614  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20615  * @cfg {Boolean} showarrow (true|false) show arrow default true
20616  * 
20617  * @constructor
20618  * Create a new TabGroup
20619  * @param {Object} config The config object
20620  */
20621
20622 Roo.bootstrap.TabGroup = function(config){
20623     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20624     if (!this.navId) {
20625         this.navId = Roo.id();
20626     }
20627     this.tabs = [];
20628     Roo.bootstrap.TabGroup.register(this);
20629     
20630 };
20631
20632 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20633     
20634     carousel : false,
20635     transition : false,
20636     bullets : 0,
20637     timer : 0,
20638     autoslide : false,
20639     slideFn : false,
20640     slideOnTouch : false,
20641     showarrow : true,
20642     
20643     getAutoCreate : function()
20644     {
20645         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20646         
20647         cfg.cls += ' tab-content';
20648         
20649         if (this.carousel) {
20650             cfg.cls += ' carousel slide';
20651             
20652             cfg.cn = [{
20653                cls : 'carousel-inner',
20654                cn : []
20655             }];
20656         
20657             if(this.bullets  && !Roo.isTouch){
20658                 
20659                 var bullets = {
20660                     cls : 'carousel-bullets',
20661                     cn : []
20662                 };
20663                
20664                 if(this.bullets_cls){
20665                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20666                 }
20667                 
20668                 bullets.cn.push({
20669                     cls : 'clear'
20670                 });
20671                 
20672                 cfg.cn[0].cn.push(bullets);
20673             }
20674             
20675             if(this.showarrow){
20676                 cfg.cn[0].cn.push({
20677                     tag : 'div',
20678                     class : 'carousel-arrow',
20679                     cn : [
20680                         {
20681                             tag : 'div',
20682                             class : 'carousel-prev',
20683                             cn : [
20684                                 {
20685                                     tag : 'i',
20686                                     class : 'fa fa-chevron-left'
20687                                 }
20688                             ]
20689                         },
20690                         {
20691                             tag : 'div',
20692                             class : 'carousel-next',
20693                             cn : [
20694                                 {
20695                                     tag : 'i',
20696                                     class : 'fa fa-chevron-right'
20697                                 }
20698                             ]
20699                         }
20700                     ]
20701                 });
20702             }
20703             
20704         }
20705         
20706         return cfg;
20707     },
20708     
20709     initEvents:  function()
20710     {
20711 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20712 //            this.el.on("touchstart", this.onTouchStart, this);
20713 //        }
20714         
20715         if(this.autoslide){
20716             var _this = this;
20717             
20718             this.slideFn = window.setInterval(function() {
20719                 _this.showPanelNext();
20720             }, this.timer);
20721         }
20722         
20723         if(this.showarrow){
20724             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20725             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20726         }
20727         
20728         
20729     },
20730     
20731 //    onTouchStart : function(e, el, o)
20732 //    {
20733 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20734 //            return;
20735 //        }
20736 //        
20737 //        this.showPanelNext();
20738 //    },
20739     
20740     
20741     getChildContainer : function()
20742     {
20743         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20744     },
20745     
20746     /**
20747     * register a Navigation item
20748     * @param {Roo.bootstrap.NavItem} the navitem to add
20749     */
20750     register : function(item)
20751     {
20752         this.tabs.push( item);
20753         item.navId = this.navId; // not really needed..
20754         this.addBullet();
20755     
20756     },
20757     
20758     getActivePanel : function()
20759     {
20760         var r = false;
20761         Roo.each(this.tabs, function(t) {
20762             if (t.active) {
20763                 r = t;
20764                 return false;
20765             }
20766             return null;
20767         });
20768         return r;
20769         
20770     },
20771     getPanelByName : function(n)
20772     {
20773         var r = false;
20774         Roo.each(this.tabs, function(t) {
20775             if (t.tabId == n) {
20776                 r = t;
20777                 return false;
20778             }
20779             return null;
20780         });
20781         return r;
20782     },
20783     indexOfPanel : function(p)
20784     {
20785         var r = false;
20786         Roo.each(this.tabs, function(t,i) {
20787             if (t.tabId == p.tabId) {
20788                 r = i;
20789                 return false;
20790             }
20791             return null;
20792         });
20793         return r;
20794     },
20795     /**
20796      * show a specific panel
20797      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20798      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20799      */
20800     showPanel : function (pan)
20801     {
20802         if(this.transition || typeof(pan) == 'undefined'){
20803             Roo.log("waiting for the transitionend");
20804             return false;
20805         }
20806         
20807         if (typeof(pan) == 'number') {
20808             pan = this.tabs[pan];
20809         }
20810         
20811         if (typeof(pan) == 'string') {
20812             pan = this.getPanelByName(pan);
20813         }
20814         
20815         var cur = this.getActivePanel();
20816         
20817         if(!pan || !cur){
20818             Roo.log('pan or acitve pan is undefined');
20819             return false;
20820         }
20821         
20822         if (pan.tabId == this.getActivePanel().tabId) {
20823             return true;
20824         }
20825         
20826         if (false === cur.fireEvent('beforedeactivate')) {
20827             return false;
20828         }
20829         
20830         if(this.bullets > 0 && !Roo.isTouch){
20831             this.setActiveBullet(this.indexOfPanel(pan));
20832         }
20833         
20834         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20835             
20836             //class="carousel-item carousel-item-next carousel-item-left"
20837             
20838             this.transition = true;
20839             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20840             var lr = dir == 'next' ? 'left' : 'right';
20841             pan.el.addClass(dir); // or prev
20842             pan.el.addClass('carousel-item-' + dir); // or prev
20843             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20844             cur.el.addClass(lr); // or right
20845             pan.el.addClass(lr);
20846             cur.el.addClass('carousel-item-' +lr); // or right
20847             pan.el.addClass('carousel-item-' +lr);
20848             
20849             
20850             var _this = this;
20851             cur.el.on('transitionend', function() {
20852                 Roo.log("trans end?");
20853                 
20854                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20855                 pan.setActive(true);
20856                 
20857                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20858                 cur.setActive(false);
20859                 
20860                 _this.transition = false;
20861                 
20862             }, this, { single:  true } );
20863             
20864             return true;
20865         }
20866         
20867         cur.setActive(false);
20868         pan.setActive(true);
20869         
20870         return true;
20871         
20872     },
20873     showPanelNext : function()
20874     {
20875         var i = this.indexOfPanel(this.getActivePanel());
20876         
20877         if (i >= this.tabs.length - 1 && !this.autoslide) {
20878             return;
20879         }
20880         
20881         if (i >= this.tabs.length - 1 && this.autoslide) {
20882             i = -1;
20883         }
20884         
20885         this.showPanel(this.tabs[i+1]);
20886     },
20887     
20888     showPanelPrev : function()
20889     {
20890         var i = this.indexOfPanel(this.getActivePanel());
20891         
20892         if (i  < 1 && !this.autoslide) {
20893             return;
20894         }
20895         
20896         if (i < 1 && this.autoslide) {
20897             i = this.tabs.length;
20898         }
20899         
20900         this.showPanel(this.tabs[i-1]);
20901     },
20902     
20903     
20904     addBullet: function()
20905     {
20906         if(!this.bullets || Roo.isTouch){
20907             return;
20908         }
20909         var ctr = this.el.select('.carousel-bullets',true).first();
20910         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20911         var bullet = ctr.createChild({
20912             cls : 'bullet bullet-' + i
20913         },ctr.dom.lastChild);
20914         
20915         
20916         var _this = this;
20917         
20918         bullet.on('click', (function(e, el, o, ii, t){
20919
20920             e.preventDefault();
20921
20922             this.showPanel(ii);
20923
20924             if(this.autoslide && this.slideFn){
20925                 clearInterval(this.slideFn);
20926                 this.slideFn = window.setInterval(function() {
20927                     _this.showPanelNext();
20928                 }, this.timer);
20929             }
20930
20931         }).createDelegate(this, [i, bullet], true));
20932                 
20933         
20934     },
20935      
20936     setActiveBullet : function(i)
20937     {
20938         if(Roo.isTouch){
20939             return;
20940         }
20941         
20942         Roo.each(this.el.select('.bullet', true).elements, function(el){
20943             el.removeClass('selected');
20944         });
20945
20946         var bullet = this.el.select('.bullet-' + i, true).first();
20947         
20948         if(!bullet){
20949             return;
20950         }
20951         
20952         bullet.addClass('selected');
20953     }
20954     
20955     
20956   
20957 });
20958
20959  
20960
20961  
20962  
20963 Roo.apply(Roo.bootstrap.TabGroup, {
20964     
20965     groups: {},
20966      /**
20967     * register a Navigation Group
20968     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20969     */
20970     register : function(navgrp)
20971     {
20972         this.groups[navgrp.navId] = navgrp;
20973         
20974     },
20975     /**
20976     * fetch a Navigation Group based on the navigation ID
20977     * if one does not exist , it will get created.
20978     * @param {string} the navgroup to add
20979     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20980     */
20981     get: function(navId) {
20982         if (typeof(this.groups[navId]) == 'undefined') {
20983             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20984         }
20985         return this.groups[navId] ;
20986     }
20987     
20988     
20989     
20990 });
20991
20992  /*
20993  * - LGPL
20994  *
20995  * TabPanel
20996  * 
20997  */
20998
20999 /**
21000  * @class Roo.bootstrap.TabPanel
21001  * @extends Roo.bootstrap.Component
21002  * Bootstrap TabPanel class
21003  * @cfg {Boolean} active panel active
21004  * @cfg {String} html panel content
21005  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21006  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21007  * @cfg {String} href click to link..
21008  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21009  * 
21010  * 
21011  * @constructor
21012  * Create a new TabPanel
21013  * @param {Object} config The config object
21014  */
21015
21016 Roo.bootstrap.TabPanel = function(config){
21017     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21018     this.addEvents({
21019         /**
21020              * @event changed
21021              * Fires when the active status changes
21022              * @param {Roo.bootstrap.TabPanel} this
21023              * @param {Boolean} state the new state
21024             
21025          */
21026         'changed': true,
21027         /**
21028              * @event beforedeactivate
21029              * Fires before a tab is de-activated - can be used to do validation on a form.
21030              * @param {Roo.bootstrap.TabPanel} this
21031              * @return {Boolean} false if there is an error
21032             
21033          */
21034         'beforedeactivate': true
21035      });
21036     
21037     this.tabId = this.tabId || Roo.id();
21038   
21039 };
21040
21041 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21042     
21043     active: false,
21044     html: false,
21045     tabId: false,
21046     navId : false,
21047     href : '',
21048     touchSlide : false,
21049     getAutoCreate : function(){
21050         
21051         
21052         var cfg = {
21053             tag: 'div',
21054             // item is needed for carousel - not sure if it has any effect otherwise
21055             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21056             html: this.html || ''
21057         };
21058         
21059         if(this.active){
21060             cfg.cls += ' active';
21061         }
21062         
21063         if(this.tabId){
21064             cfg.tabId = this.tabId;
21065         }
21066         
21067         
21068         
21069         return cfg;
21070     },
21071     
21072     initEvents:  function()
21073     {
21074         var p = this.parent();
21075         
21076         this.navId = this.navId || p.navId;
21077         
21078         if (typeof(this.navId) != 'undefined') {
21079             // not really needed.. but just in case.. parent should be a NavGroup.
21080             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21081             
21082             tg.register(this);
21083             
21084             var i = tg.tabs.length - 1;
21085             
21086             if(this.active && tg.bullets > 0 && i < tg.bullets){
21087                 tg.setActiveBullet(i);
21088             }
21089         }
21090         
21091         this.el.on('click', this.onClick, this);
21092         
21093         if(Roo.isTouch && this.touchSlide){
21094             this.el.on("touchstart", this.onTouchStart, this);
21095             this.el.on("touchmove", this.onTouchMove, this);
21096             this.el.on("touchend", this.onTouchEnd, this);
21097         }
21098         
21099     },
21100     
21101     onRender : function(ct, position)
21102     {
21103         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21104     },
21105     
21106     setActive : function(state)
21107     {
21108         Roo.log("panel - set active " + this.tabId + "=" + state);
21109         
21110         this.active = state;
21111         if (!state) {
21112             this.el.removeClass('active');
21113             
21114         } else  if (!this.el.hasClass('active')) {
21115             this.el.addClass('active');
21116         }
21117         
21118         this.fireEvent('changed', this, state);
21119     },
21120     
21121     onClick : function(e)
21122     {
21123         e.preventDefault();
21124         
21125         if(!this.href.length){
21126             return;
21127         }
21128         
21129         window.location.href = this.href;
21130     },
21131     
21132     startX : 0,
21133     startY : 0,
21134     endX : 0,
21135     endY : 0,
21136     swiping : false,
21137     
21138     onTouchStart : function(e)
21139     {
21140         this.swiping = false;
21141         
21142         this.startX = e.browserEvent.touches[0].clientX;
21143         this.startY = e.browserEvent.touches[0].clientY;
21144     },
21145     
21146     onTouchMove : function(e)
21147     {
21148         this.swiping = true;
21149         
21150         this.endX = e.browserEvent.touches[0].clientX;
21151         this.endY = e.browserEvent.touches[0].clientY;
21152     },
21153     
21154     onTouchEnd : function(e)
21155     {
21156         if(!this.swiping){
21157             this.onClick(e);
21158             return;
21159         }
21160         
21161         var tabGroup = this.parent();
21162         
21163         if(this.endX > this.startX){ // swiping right
21164             tabGroup.showPanelPrev();
21165             return;
21166         }
21167         
21168         if(this.startX > this.endX){ // swiping left
21169             tabGroup.showPanelNext();
21170             return;
21171         }
21172     }
21173     
21174     
21175 });
21176  
21177
21178  
21179
21180  /*
21181  * - LGPL
21182  *
21183  * DateField
21184  * 
21185  */
21186
21187 /**
21188  * @class Roo.bootstrap.DateField
21189  * @extends Roo.bootstrap.Input
21190  * Bootstrap DateField class
21191  * @cfg {Number} weekStart default 0
21192  * @cfg {String} viewMode default empty, (months|years)
21193  * @cfg {String} minViewMode default empty, (months|years)
21194  * @cfg {Number} startDate default -Infinity
21195  * @cfg {Number} endDate default Infinity
21196  * @cfg {Boolean} todayHighlight default false
21197  * @cfg {Boolean} todayBtn default false
21198  * @cfg {Boolean} calendarWeeks default false
21199  * @cfg {Object} daysOfWeekDisabled default empty
21200  * @cfg {Boolean} singleMode default false (true | false)
21201  * 
21202  * @cfg {Boolean} keyboardNavigation default true
21203  * @cfg {String} language default en
21204  * 
21205  * @constructor
21206  * Create a new DateField
21207  * @param {Object} config The config object
21208  */
21209
21210 Roo.bootstrap.DateField = function(config){
21211     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21212      this.addEvents({
21213             /**
21214              * @event show
21215              * Fires when this field show.
21216              * @param {Roo.bootstrap.DateField} this
21217              * @param {Mixed} date The date value
21218              */
21219             show : true,
21220             /**
21221              * @event show
21222              * Fires when this field hide.
21223              * @param {Roo.bootstrap.DateField} this
21224              * @param {Mixed} date The date value
21225              */
21226             hide : true,
21227             /**
21228              * @event select
21229              * Fires when select a date.
21230              * @param {Roo.bootstrap.DateField} this
21231              * @param {Mixed} date The date value
21232              */
21233             select : true,
21234             /**
21235              * @event beforeselect
21236              * Fires when before select a date.
21237              * @param {Roo.bootstrap.DateField} this
21238              * @param {Mixed} date The date value
21239              */
21240             beforeselect : true
21241         });
21242 };
21243
21244 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21245     
21246     /**
21247      * @cfg {String} format
21248      * The default date format string which can be overriden for localization support.  The format must be
21249      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21250      */
21251     format : "m/d/y",
21252     /**
21253      * @cfg {String} altFormats
21254      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21255      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21256      */
21257     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21258     
21259     weekStart : 0,
21260     
21261     viewMode : '',
21262     
21263     minViewMode : '',
21264     
21265     todayHighlight : false,
21266     
21267     todayBtn: false,
21268     
21269     language: 'en',
21270     
21271     keyboardNavigation: true,
21272     
21273     calendarWeeks: false,
21274     
21275     startDate: -Infinity,
21276     
21277     endDate: Infinity,
21278     
21279     daysOfWeekDisabled: [],
21280     
21281     _events: [],
21282     
21283     singleMode : false,
21284     
21285     UTCDate: function()
21286     {
21287         return new Date(Date.UTC.apply(Date, arguments));
21288     },
21289     
21290     UTCToday: function()
21291     {
21292         var today = new Date();
21293         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21294     },
21295     
21296     getDate: function() {
21297             var d = this.getUTCDate();
21298             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21299     },
21300     
21301     getUTCDate: function() {
21302             return this.date;
21303     },
21304     
21305     setDate: function(d) {
21306             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21307     },
21308     
21309     setUTCDate: function(d) {
21310             this.date = d;
21311             this.setValue(this.formatDate(this.date));
21312     },
21313         
21314     onRender: function(ct, position)
21315     {
21316         
21317         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21318         
21319         this.language = this.language || 'en';
21320         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21321         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21322         
21323         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21324         this.format = this.format || 'm/d/y';
21325         this.isInline = false;
21326         this.isInput = true;
21327         this.component = this.el.select('.add-on', true).first() || false;
21328         this.component = (this.component && this.component.length === 0) ? false : this.component;
21329         this.hasInput = this.component && this.inputEl().length;
21330         
21331         if (typeof(this.minViewMode === 'string')) {
21332             switch (this.minViewMode) {
21333                 case 'months':
21334                     this.minViewMode = 1;
21335                     break;
21336                 case 'years':
21337                     this.minViewMode = 2;
21338                     break;
21339                 default:
21340                     this.minViewMode = 0;
21341                     break;
21342             }
21343         }
21344         
21345         if (typeof(this.viewMode === 'string')) {
21346             switch (this.viewMode) {
21347                 case 'months':
21348                     this.viewMode = 1;
21349                     break;
21350                 case 'years':
21351                     this.viewMode = 2;
21352                     break;
21353                 default:
21354                     this.viewMode = 0;
21355                     break;
21356             }
21357         }
21358                 
21359         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21360         
21361 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21362         
21363         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21364         
21365         this.picker().on('mousedown', this.onMousedown, this);
21366         this.picker().on('click', this.onClick, this);
21367         
21368         this.picker().addClass('datepicker-dropdown');
21369         
21370         this.startViewMode = this.viewMode;
21371         
21372         if(this.singleMode){
21373             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21374                 v.setVisibilityMode(Roo.Element.DISPLAY);
21375                 v.hide();
21376             });
21377             
21378             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21379                 v.setStyle('width', '189px');
21380             });
21381         }
21382         
21383         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21384             if(!this.calendarWeeks){
21385                 v.remove();
21386                 return;
21387             }
21388             
21389             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21390             v.attr('colspan', function(i, val){
21391                 return parseInt(val) + 1;
21392             });
21393         });
21394                         
21395         
21396         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21397         
21398         this.setStartDate(this.startDate);
21399         this.setEndDate(this.endDate);
21400         
21401         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21402         
21403         this.fillDow();
21404         this.fillMonths();
21405         this.update();
21406         this.showMode();
21407         
21408         if(this.isInline) {
21409             this.showPopup();
21410         }
21411     },
21412     
21413     picker : function()
21414     {
21415         return this.pickerEl;
21416 //        return this.el.select('.datepicker', true).first();
21417     },
21418     
21419     fillDow: function()
21420     {
21421         var dowCnt = this.weekStart;
21422         
21423         var dow = {
21424             tag: 'tr',
21425             cn: [
21426                 
21427             ]
21428         };
21429         
21430         if(this.calendarWeeks){
21431             dow.cn.push({
21432                 tag: 'th',
21433                 cls: 'cw',
21434                 html: '&nbsp;'
21435             })
21436         }
21437         
21438         while (dowCnt < this.weekStart + 7) {
21439             dow.cn.push({
21440                 tag: 'th',
21441                 cls: 'dow',
21442                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21443             });
21444         }
21445         
21446         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21447     },
21448     
21449     fillMonths: function()
21450     {    
21451         var i = 0;
21452         var months = this.picker().select('>.datepicker-months td', true).first();
21453         
21454         months.dom.innerHTML = '';
21455         
21456         while (i < 12) {
21457             var month = {
21458                 tag: 'span',
21459                 cls: 'month',
21460                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21461             };
21462             
21463             months.createChild(month);
21464         }
21465         
21466     },
21467     
21468     update: function()
21469     {
21470         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;
21471         
21472         if (this.date < this.startDate) {
21473             this.viewDate = new Date(this.startDate);
21474         } else if (this.date > this.endDate) {
21475             this.viewDate = new Date(this.endDate);
21476         } else {
21477             this.viewDate = new Date(this.date);
21478         }
21479         
21480         this.fill();
21481     },
21482     
21483     fill: function() 
21484     {
21485         var d = new Date(this.viewDate),
21486                 year = d.getUTCFullYear(),
21487                 month = d.getUTCMonth(),
21488                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21489                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21490                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21491                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21492                 currentDate = this.date && this.date.valueOf(),
21493                 today = this.UTCToday();
21494         
21495         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21496         
21497 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21498         
21499 //        this.picker.select('>tfoot th.today').
21500 //                                              .text(dates[this.language].today)
21501 //                                              .toggle(this.todayBtn !== false);
21502     
21503         this.updateNavArrows();
21504         this.fillMonths();
21505                                                 
21506         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21507         
21508         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21509          
21510         prevMonth.setUTCDate(day);
21511         
21512         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21513         
21514         var nextMonth = new Date(prevMonth);
21515         
21516         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21517         
21518         nextMonth = nextMonth.valueOf();
21519         
21520         var fillMonths = false;
21521         
21522         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21523         
21524         while(prevMonth.valueOf() <= nextMonth) {
21525             var clsName = '';
21526             
21527             if (prevMonth.getUTCDay() === this.weekStart) {
21528                 if(fillMonths){
21529                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21530                 }
21531                     
21532                 fillMonths = {
21533                     tag: 'tr',
21534                     cn: []
21535                 };
21536                 
21537                 if(this.calendarWeeks){
21538                     // ISO 8601: First week contains first thursday.
21539                     // ISO also states week starts on Monday, but we can be more abstract here.
21540                     var
21541                     // Start of current week: based on weekstart/current date
21542                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21543                     // Thursday of this week
21544                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21545                     // First Thursday of year, year from thursday
21546                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21547                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21548                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21549                     
21550                     fillMonths.cn.push({
21551                         tag: 'td',
21552                         cls: 'cw',
21553                         html: calWeek
21554                     });
21555                 }
21556             }
21557             
21558             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21559                 clsName += ' old';
21560             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21561                 clsName += ' new';
21562             }
21563             if (this.todayHighlight &&
21564                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21565                 prevMonth.getUTCMonth() == today.getMonth() &&
21566                 prevMonth.getUTCDate() == today.getDate()) {
21567                 clsName += ' today';
21568             }
21569             
21570             if (currentDate && prevMonth.valueOf() === currentDate) {
21571                 clsName += ' active';
21572             }
21573             
21574             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21575                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21576                     clsName += ' disabled';
21577             }
21578             
21579             fillMonths.cn.push({
21580                 tag: 'td',
21581                 cls: 'day ' + clsName,
21582                 html: prevMonth.getDate()
21583             });
21584             
21585             prevMonth.setDate(prevMonth.getDate()+1);
21586         }
21587           
21588         var currentYear = this.date && this.date.getUTCFullYear();
21589         var currentMonth = this.date && this.date.getUTCMonth();
21590         
21591         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21592         
21593         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21594             v.removeClass('active');
21595             
21596             if(currentYear === year && k === currentMonth){
21597                 v.addClass('active');
21598             }
21599             
21600             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21601                 v.addClass('disabled');
21602             }
21603             
21604         });
21605         
21606         
21607         year = parseInt(year/10, 10) * 10;
21608         
21609         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21610         
21611         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21612         
21613         year -= 1;
21614         for (var i = -1; i < 11; i++) {
21615             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21616                 tag: 'span',
21617                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21618                 html: year
21619             });
21620             
21621             year += 1;
21622         }
21623     },
21624     
21625     showMode: function(dir) 
21626     {
21627         if (dir) {
21628             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21629         }
21630         
21631         Roo.each(this.picker().select('>div',true).elements, function(v){
21632             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21633             v.hide();
21634         });
21635         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21636     },
21637     
21638     place: function()
21639     {
21640         if(this.isInline) {
21641             return;
21642         }
21643         
21644         this.picker().removeClass(['bottom', 'top']);
21645         
21646         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21647             /*
21648              * place to the top of element!
21649              *
21650              */
21651             
21652             this.picker().addClass('top');
21653             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21654             
21655             return;
21656         }
21657         
21658         this.picker().addClass('bottom');
21659         
21660         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21661     },
21662     
21663     parseDate : function(value)
21664     {
21665         if(!value || value instanceof Date){
21666             return value;
21667         }
21668         var v = Date.parseDate(value, this.format);
21669         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21670             v = Date.parseDate(value, 'Y-m-d');
21671         }
21672         if(!v && this.altFormats){
21673             if(!this.altFormatsArray){
21674                 this.altFormatsArray = this.altFormats.split("|");
21675             }
21676             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21677                 v = Date.parseDate(value, this.altFormatsArray[i]);
21678             }
21679         }
21680         return v;
21681     },
21682     
21683     formatDate : function(date, fmt)
21684     {   
21685         return (!date || !(date instanceof Date)) ?
21686         date : date.dateFormat(fmt || this.format);
21687     },
21688     
21689     onFocus : function()
21690     {
21691         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21692         this.showPopup();
21693     },
21694     
21695     onBlur : function()
21696     {
21697         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21698         
21699         var d = this.inputEl().getValue();
21700         
21701         this.setValue(d);
21702                 
21703         this.hidePopup();
21704     },
21705     
21706     showPopup : function()
21707     {
21708         this.picker().show();
21709         this.update();
21710         this.place();
21711         
21712         this.fireEvent('showpopup', this, this.date);
21713     },
21714     
21715     hidePopup : function()
21716     {
21717         if(this.isInline) {
21718             return;
21719         }
21720         this.picker().hide();
21721         this.viewMode = this.startViewMode;
21722         this.showMode();
21723         
21724         this.fireEvent('hidepopup', this, this.date);
21725         
21726     },
21727     
21728     onMousedown: function(e)
21729     {
21730         e.stopPropagation();
21731         e.preventDefault();
21732     },
21733     
21734     keyup: function(e)
21735     {
21736         Roo.bootstrap.DateField.superclass.keyup.call(this);
21737         this.update();
21738     },
21739
21740     setValue: function(v)
21741     {
21742         if(this.fireEvent('beforeselect', this, v) !== false){
21743             var d = new Date(this.parseDate(v) ).clearTime();
21744         
21745             if(isNaN(d.getTime())){
21746                 this.date = this.viewDate = '';
21747                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21748                 return;
21749             }
21750
21751             v = this.formatDate(d);
21752
21753             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21754
21755             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21756
21757             this.update();
21758
21759             this.fireEvent('select', this, this.date);
21760         }
21761     },
21762     
21763     getValue: function()
21764     {
21765         return this.formatDate(this.date);
21766     },
21767     
21768     fireKey: function(e)
21769     {
21770         if (!this.picker().isVisible()){
21771             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21772                 this.showPopup();
21773             }
21774             return;
21775         }
21776         
21777         var dateChanged = false,
21778         dir, day, month,
21779         newDate, newViewDate;
21780         
21781         switch(e.keyCode){
21782             case 27: // escape
21783                 this.hidePopup();
21784                 e.preventDefault();
21785                 break;
21786             case 37: // left
21787             case 39: // right
21788                 if (!this.keyboardNavigation) {
21789                     break;
21790                 }
21791                 dir = e.keyCode == 37 ? -1 : 1;
21792                 
21793                 if (e.ctrlKey){
21794                     newDate = this.moveYear(this.date, dir);
21795                     newViewDate = this.moveYear(this.viewDate, dir);
21796                 } else if (e.shiftKey){
21797                     newDate = this.moveMonth(this.date, dir);
21798                     newViewDate = this.moveMonth(this.viewDate, dir);
21799                 } else {
21800                     newDate = new Date(this.date);
21801                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21802                     newViewDate = new Date(this.viewDate);
21803                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21804                 }
21805                 if (this.dateWithinRange(newDate)){
21806                     this.date = newDate;
21807                     this.viewDate = newViewDate;
21808                     this.setValue(this.formatDate(this.date));
21809 //                    this.update();
21810                     e.preventDefault();
21811                     dateChanged = true;
21812                 }
21813                 break;
21814             case 38: // up
21815             case 40: // down
21816                 if (!this.keyboardNavigation) {
21817                     break;
21818                 }
21819                 dir = e.keyCode == 38 ? -1 : 1;
21820                 if (e.ctrlKey){
21821                     newDate = this.moveYear(this.date, dir);
21822                     newViewDate = this.moveYear(this.viewDate, dir);
21823                 } else if (e.shiftKey){
21824                     newDate = this.moveMonth(this.date, dir);
21825                     newViewDate = this.moveMonth(this.viewDate, dir);
21826                 } else {
21827                     newDate = new Date(this.date);
21828                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21829                     newViewDate = new Date(this.viewDate);
21830                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21831                 }
21832                 if (this.dateWithinRange(newDate)){
21833                     this.date = newDate;
21834                     this.viewDate = newViewDate;
21835                     this.setValue(this.formatDate(this.date));
21836 //                    this.update();
21837                     e.preventDefault();
21838                     dateChanged = true;
21839                 }
21840                 break;
21841             case 13: // enter
21842                 this.setValue(this.formatDate(this.date));
21843                 this.hidePopup();
21844                 e.preventDefault();
21845                 break;
21846             case 9: // tab
21847                 this.setValue(this.formatDate(this.date));
21848                 this.hidePopup();
21849                 break;
21850             case 16: // shift
21851             case 17: // ctrl
21852             case 18: // alt
21853                 break;
21854             default :
21855                 this.hidePopup();
21856                 
21857         }
21858     },
21859     
21860     
21861     onClick: function(e) 
21862     {
21863         e.stopPropagation();
21864         e.preventDefault();
21865         
21866         var target = e.getTarget();
21867         
21868         if(target.nodeName.toLowerCase() === 'i'){
21869             target = Roo.get(target).dom.parentNode;
21870         }
21871         
21872         var nodeName = target.nodeName;
21873         var className = target.className;
21874         var html = target.innerHTML;
21875         //Roo.log(nodeName);
21876         
21877         switch(nodeName.toLowerCase()) {
21878             case 'th':
21879                 switch(className) {
21880                     case 'switch':
21881                         this.showMode(1);
21882                         break;
21883                     case 'prev':
21884                     case 'next':
21885                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21886                         switch(this.viewMode){
21887                                 case 0:
21888                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21889                                         break;
21890                                 case 1:
21891                                 case 2:
21892                                         this.viewDate = this.moveYear(this.viewDate, dir);
21893                                         break;
21894                         }
21895                         this.fill();
21896                         break;
21897                     case 'today':
21898                         var date = new Date();
21899                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21900 //                        this.fill()
21901                         this.setValue(this.formatDate(this.date));
21902                         
21903                         this.hidePopup();
21904                         break;
21905                 }
21906                 break;
21907             case 'span':
21908                 if (className.indexOf('disabled') < 0) {
21909                     this.viewDate.setUTCDate(1);
21910                     if (className.indexOf('month') > -1) {
21911                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21912                     } else {
21913                         var year = parseInt(html, 10) || 0;
21914                         this.viewDate.setUTCFullYear(year);
21915                         
21916                     }
21917                     
21918                     if(this.singleMode){
21919                         this.setValue(this.formatDate(this.viewDate));
21920                         this.hidePopup();
21921                         return;
21922                     }
21923                     
21924                     this.showMode(-1);
21925                     this.fill();
21926                 }
21927                 break;
21928                 
21929             case 'td':
21930                 //Roo.log(className);
21931                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21932                     var day = parseInt(html, 10) || 1;
21933                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21934                         month = (this.viewDate || new Date()).getUTCMonth();
21935
21936                     if (className.indexOf('old') > -1) {
21937                         if(month === 0 ){
21938                             month = 11;
21939                             year -= 1;
21940                         }else{
21941                             month -= 1;
21942                         }
21943                     } else if (className.indexOf('new') > -1) {
21944                         if (month == 11) {
21945                             month = 0;
21946                             year += 1;
21947                         } else {
21948                             month += 1;
21949                         }
21950                     }
21951                     //Roo.log([year,month,day]);
21952                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21953                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21954 //                    this.fill();
21955                     //Roo.log(this.formatDate(this.date));
21956                     this.setValue(this.formatDate(this.date));
21957                     this.hidePopup();
21958                 }
21959                 break;
21960         }
21961     },
21962     
21963     setStartDate: function(startDate)
21964     {
21965         this.startDate = startDate || -Infinity;
21966         if (this.startDate !== -Infinity) {
21967             this.startDate = this.parseDate(this.startDate);
21968         }
21969         this.update();
21970         this.updateNavArrows();
21971     },
21972
21973     setEndDate: function(endDate)
21974     {
21975         this.endDate = endDate || Infinity;
21976         if (this.endDate !== Infinity) {
21977             this.endDate = this.parseDate(this.endDate);
21978         }
21979         this.update();
21980         this.updateNavArrows();
21981     },
21982     
21983     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21984     {
21985         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21986         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21987             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21988         }
21989         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21990             return parseInt(d, 10);
21991         });
21992         this.update();
21993         this.updateNavArrows();
21994     },
21995     
21996     updateNavArrows: function() 
21997     {
21998         if(this.singleMode){
21999             return;
22000         }
22001         
22002         var d = new Date(this.viewDate),
22003         year = d.getUTCFullYear(),
22004         month = d.getUTCMonth();
22005         
22006         Roo.each(this.picker().select('.prev', true).elements, function(v){
22007             v.show();
22008             switch (this.viewMode) {
22009                 case 0:
22010
22011                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22012                         v.hide();
22013                     }
22014                     break;
22015                 case 1:
22016                 case 2:
22017                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22018                         v.hide();
22019                     }
22020                     break;
22021             }
22022         });
22023         
22024         Roo.each(this.picker().select('.next', true).elements, function(v){
22025             v.show();
22026             switch (this.viewMode) {
22027                 case 0:
22028
22029                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22030                         v.hide();
22031                     }
22032                     break;
22033                 case 1:
22034                 case 2:
22035                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22036                         v.hide();
22037                     }
22038                     break;
22039             }
22040         })
22041     },
22042     
22043     moveMonth: function(date, dir)
22044     {
22045         if (!dir) {
22046             return date;
22047         }
22048         var new_date = new Date(date.valueOf()),
22049         day = new_date.getUTCDate(),
22050         month = new_date.getUTCMonth(),
22051         mag = Math.abs(dir),
22052         new_month, test;
22053         dir = dir > 0 ? 1 : -1;
22054         if (mag == 1){
22055             test = dir == -1
22056             // If going back one month, make sure month is not current month
22057             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22058             ? function(){
22059                 return new_date.getUTCMonth() == month;
22060             }
22061             // If going forward one month, make sure month is as expected
22062             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22063             : function(){
22064                 return new_date.getUTCMonth() != new_month;
22065             };
22066             new_month = month + dir;
22067             new_date.setUTCMonth(new_month);
22068             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22069             if (new_month < 0 || new_month > 11) {
22070                 new_month = (new_month + 12) % 12;
22071             }
22072         } else {
22073             // For magnitudes >1, move one month at a time...
22074             for (var i=0; i<mag; i++) {
22075                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22076                 new_date = this.moveMonth(new_date, dir);
22077             }
22078             // ...then reset the day, keeping it in the new month
22079             new_month = new_date.getUTCMonth();
22080             new_date.setUTCDate(day);
22081             test = function(){
22082                 return new_month != new_date.getUTCMonth();
22083             };
22084         }
22085         // Common date-resetting loop -- if date is beyond end of month, make it
22086         // end of month
22087         while (test()){
22088             new_date.setUTCDate(--day);
22089             new_date.setUTCMonth(new_month);
22090         }
22091         return new_date;
22092     },
22093
22094     moveYear: function(date, dir)
22095     {
22096         return this.moveMonth(date, dir*12);
22097     },
22098
22099     dateWithinRange: function(date)
22100     {
22101         return date >= this.startDate && date <= this.endDate;
22102     },
22103
22104     
22105     remove: function() 
22106     {
22107         this.picker().remove();
22108     },
22109     
22110     validateValue : function(value)
22111     {
22112         if(this.getVisibilityEl().hasClass('hidden')){
22113             return true;
22114         }
22115         
22116         if(value.length < 1)  {
22117             if(this.allowBlank){
22118                 return true;
22119             }
22120             return false;
22121         }
22122         
22123         if(value.length < this.minLength){
22124             return false;
22125         }
22126         if(value.length > this.maxLength){
22127             return false;
22128         }
22129         if(this.vtype){
22130             var vt = Roo.form.VTypes;
22131             if(!vt[this.vtype](value, this)){
22132                 return false;
22133             }
22134         }
22135         if(typeof this.validator == "function"){
22136             var msg = this.validator(value);
22137             if(msg !== true){
22138                 return false;
22139             }
22140         }
22141         
22142         if(this.regex && !this.regex.test(value)){
22143             return false;
22144         }
22145         
22146         if(typeof(this.parseDate(value)) == 'undefined'){
22147             return false;
22148         }
22149         
22150         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22151             return false;
22152         }      
22153         
22154         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22155             return false;
22156         } 
22157         
22158         
22159         return true;
22160     },
22161     
22162     reset : function()
22163     {
22164         this.date = this.viewDate = '';
22165         
22166         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22167     }
22168    
22169 });
22170
22171 Roo.apply(Roo.bootstrap.DateField,  {
22172     
22173     head : {
22174         tag: 'thead',
22175         cn: [
22176         {
22177             tag: 'tr',
22178             cn: [
22179             {
22180                 tag: 'th',
22181                 cls: 'prev',
22182                 html: '<i class="fa fa-arrow-left"/>'
22183             },
22184             {
22185                 tag: 'th',
22186                 cls: 'switch',
22187                 colspan: '5'
22188             },
22189             {
22190                 tag: 'th',
22191                 cls: 'next',
22192                 html: '<i class="fa fa-arrow-right"/>'
22193             }
22194
22195             ]
22196         }
22197         ]
22198     },
22199     
22200     content : {
22201         tag: 'tbody',
22202         cn: [
22203         {
22204             tag: 'tr',
22205             cn: [
22206             {
22207                 tag: 'td',
22208                 colspan: '7'
22209             }
22210             ]
22211         }
22212         ]
22213     },
22214     
22215     footer : {
22216         tag: 'tfoot',
22217         cn: [
22218         {
22219             tag: 'tr',
22220             cn: [
22221             {
22222                 tag: 'th',
22223                 colspan: '7',
22224                 cls: 'today'
22225             }
22226                     
22227             ]
22228         }
22229         ]
22230     },
22231     
22232     dates:{
22233         en: {
22234             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22235             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22236             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22237             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22238             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22239             today: "Today"
22240         }
22241     },
22242     
22243     modes: [
22244     {
22245         clsName: 'days',
22246         navFnc: 'Month',
22247         navStep: 1
22248     },
22249     {
22250         clsName: 'months',
22251         navFnc: 'FullYear',
22252         navStep: 1
22253     },
22254     {
22255         clsName: 'years',
22256         navFnc: 'FullYear',
22257         navStep: 10
22258     }]
22259 });
22260
22261 Roo.apply(Roo.bootstrap.DateField,  {
22262   
22263     template : {
22264         tag: 'div',
22265         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22266         cn: [
22267         {
22268             tag: 'div',
22269             cls: 'datepicker-days',
22270             cn: [
22271             {
22272                 tag: 'table',
22273                 cls: 'table-condensed',
22274                 cn:[
22275                 Roo.bootstrap.DateField.head,
22276                 {
22277                     tag: 'tbody'
22278                 },
22279                 Roo.bootstrap.DateField.footer
22280                 ]
22281             }
22282             ]
22283         },
22284         {
22285             tag: 'div',
22286             cls: 'datepicker-months',
22287             cn: [
22288             {
22289                 tag: 'table',
22290                 cls: 'table-condensed',
22291                 cn:[
22292                 Roo.bootstrap.DateField.head,
22293                 Roo.bootstrap.DateField.content,
22294                 Roo.bootstrap.DateField.footer
22295                 ]
22296             }
22297             ]
22298         },
22299         {
22300             tag: 'div',
22301             cls: 'datepicker-years',
22302             cn: [
22303             {
22304                 tag: 'table',
22305                 cls: 'table-condensed',
22306                 cn:[
22307                 Roo.bootstrap.DateField.head,
22308                 Roo.bootstrap.DateField.content,
22309                 Roo.bootstrap.DateField.footer
22310                 ]
22311             }
22312             ]
22313         }
22314         ]
22315     }
22316 });
22317
22318  
22319
22320  /*
22321  * - LGPL
22322  *
22323  * TimeField
22324  * 
22325  */
22326
22327 /**
22328  * @class Roo.bootstrap.TimeField
22329  * @extends Roo.bootstrap.Input
22330  * Bootstrap DateField class
22331  * 
22332  * 
22333  * @constructor
22334  * Create a new TimeField
22335  * @param {Object} config The config object
22336  */
22337
22338 Roo.bootstrap.TimeField = function(config){
22339     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22340     this.addEvents({
22341             /**
22342              * @event show
22343              * Fires when this field show.
22344              * @param {Roo.bootstrap.DateField} thisthis
22345              * @param {Mixed} date The date value
22346              */
22347             show : true,
22348             /**
22349              * @event show
22350              * Fires when this field hide.
22351              * @param {Roo.bootstrap.DateField} this
22352              * @param {Mixed} date The date value
22353              */
22354             hide : true,
22355             /**
22356              * @event select
22357              * Fires when select a date.
22358              * @param {Roo.bootstrap.DateField} this
22359              * @param {Mixed} date The date value
22360              */
22361             select : true
22362         });
22363 };
22364
22365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22366     
22367     /**
22368      * @cfg {String} format
22369      * The default time format string which can be overriden for localization support.  The format must be
22370      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22371      */
22372     format : "H:i",
22373
22374     getAutoCreate : function()
22375     {
22376         this.after = '<i class="fa far fa-clock"></i>';
22377         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22378         
22379          
22380     },
22381     onRender: function(ct, position)
22382     {
22383         
22384         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22385                 
22386         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22387         
22388         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22389         
22390         this.pop = this.picker().select('>.datepicker-time',true).first();
22391         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22392         
22393         this.picker().on('mousedown', this.onMousedown, this);
22394         this.picker().on('click', this.onClick, this);
22395         
22396         this.picker().addClass('datepicker-dropdown');
22397     
22398         this.fillTime();
22399         this.update();
22400             
22401         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22402         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22403         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22404         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22405         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22406         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22407
22408     },
22409     
22410     fireKey: function(e){
22411         if (!this.picker().isVisible()){
22412             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22413                 this.show();
22414             }
22415             return;
22416         }
22417
22418         e.preventDefault();
22419         
22420         switch(e.keyCode){
22421             case 27: // escape
22422                 this.hide();
22423                 break;
22424             case 37: // left
22425             case 39: // right
22426                 this.onTogglePeriod();
22427                 break;
22428             case 38: // up
22429                 this.onIncrementMinutes();
22430                 break;
22431             case 40: // down
22432                 this.onDecrementMinutes();
22433                 break;
22434             case 13: // enter
22435             case 9: // tab
22436                 this.setTime();
22437                 break;
22438         }
22439     },
22440     
22441     onClick: function(e) {
22442         e.stopPropagation();
22443         e.preventDefault();
22444     },
22445     
22446     picker : function()
22447     {
22448         return this.pickerEl;
22449     },
22450     
22451     fillTime: function()
22452     {    
22453         var time = this.pop.select('tbody', true).first();
22454         
22455         time.dom.innerHTML = '';
22456         
22457         time.createChild({
22458             tag: 'tr',
22459             cn: [
22460                 {
22461                     tag: 'td',
22462                     cn: [
22463                         {
22464                             tag: 'a',
22465                             href: '#',
22466                             cls: 'btn',
22467                             cn: [
22468                                 {
22469                                     tag: 'i',
22470                                     cls: 'hours-up fa fas fa-chevron-up'
22471                                 }
22472                             ]
22473                         } 
22474                     ]
22475                 },
22476                 {
22477                     tag: 'td',
22478                     cls: 'separator'
22479                 },
22480                 {
22481                     tag: 'td',
22482                     cn: [
22483                         {
22484                             tag: 'a',
22485                             href: '#',
22486                             cls: 'btn',
22487                             cn: [
22488                                 {
22489                                     tag: 'i',
22490                                     cls: 'minutes-up fa fas fa-chevron-up'
22491                                 }
22492                             ]
22493                         }
22494                     ]
22495                 },
22496                 {
22497                     tag: 'td',
22498                     cls: 'separator'
22499                 }
22500             ]
22501         });
22502         
22503         time.createChild({
22504             tag: 'tr',
22505             cn: [
22506                 {
22507                     tag: 'td',
22508                     cn: [
22509                         {
22510                             tag: 'span',
22511                             cls: 'timepicker-hour',
22512                             html: '00'
22513                         }  
22514                     ]
22515                 },
22516                 {
22517                     tag: 'td',
22518                     cls: 'separator',
22519                     html: ':'
22520                 },
22521                 {
22522                     tag: 'td',
22523                     cn: [
22524                         {
22525                             tag: 'span',
22526                             cls: 'timepicker-minute',
22527                             html: '00'
22528                         }  
22529                     ]
22530                 },
22531                 {
22532                     tag: 'td',
22533                     cls: 'separator'
22534                 },
22535                 {
22536                     tag: 'td',
22537                     cn: [
22538                         {
22539                             tag: 'button',
22540                             type: 'button',
22541                             cls: 'btn btn-primary period',
22542                             html: 'AM'
22543                             
22544                         }
22545                     ]
22546                 }
22547             ]
22548         });
22549         
22550         time.createChild({
22551             tag: 'tr',
22552             cn: [
22553                 {
22554                     tag: 'td',
22555                     cn: [
22556                         {
22557                             tag: 'a',
22558                             href: '#',
22559                             cls: 'btn',
22560                             cn: [
22561                                 {
22562                                     tag: 'span',
22563                                     cls: 'hours-down fa fas fa-chevron-down'
22564                                 }
22565                             ]
22566                         }
22567                     ]
22568                 },
22569                 {
22570                     tag: 'td',
22571                     cls: 'separator'
22572                 },
22573                 {
22574                     tag: 'td',
22575                     cn: [
22576                         {
22577                             tag: 'a',
22578                             href: '#',
22579                             cls: 'btn',
22580                             cn: [
22581                                 {
22582                                     tag: 'span',
22583                                     cls: 'minutes-down fa fas fa-chevron-down'
22584                                 }
22585                             ]
22586                         }
22587                     ]
22588                 },
22589                 {
22590                     tag: 'td',
22591                     cls: 'separator'
22592                 }
22593             ]
22594         });
22595         
22596     },
22597     
22598     update: function()
22599     {
22600         
22601         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22602         
22603         this.fill();
22604     },
22605     
22606     fill: function() 
22607     {
22608         var hours = this.time.getHours();
22609         var minutes = this.time.getMinutes();
22610         var period = 'AM';
22611         
22612         if(hours > 11){
22613             period = 'PM';
22614         }
22615         
22616         if(hours == 0){
22617             hours = 12;
22618         }
22619         
22620         
22621         if(hours > 12){
22622             hours = hours - 12;
22623         }
22624         
22625         if(hours < 10){
22626             hours = '0' + hours;
22627         }
22628         
22629         if(minutes < 10){
22630             minutes = '0' + minutes;
22631         }
22632         
22633         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22634         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22635         this.pop.select('button', true).first().dom.innerHTML = period;
22636         
22637     },
22638     
22639     place: function()
22640     {   
22641         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22642         
22643         var cls = ['bottom'];
22644         
22645         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22646             cls.pop();
22647             cls.push('top');
22648         }
22649         
22650         cls.push('right');
22651         
22652         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22653             cls.pop();
22654             cls.push('left');
22655         }
22656         //this.picker().setXY(20000,20000);
22657         this.picker().addClass(cls.join('-'));
22658         
22659         var _this = this;
22660         
22661         Roo.each(cls, function(c){
22662             if(c == 'bottom'){
22663                 (function() {
22664                  //  
22665                 }).defer(200);
22666                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22667                 //_this.picker().setTop(_this.inputEl().getHeight());
22668                 return;
22669             }
22670             if(c == 'top'){
22671                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22672                 
22673                 //_this.picker().setTop(0 - _this.picker().getHeight());
22674                 return;
22675             }
22676             /*
22677             if(c == 'left'){
22678                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22679                 return;
22680             }
22681             if(c == 'right'){
22682                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22683                 return;
22684             }
22685             */
22686         });
22687         
22688     },
22689   
22690     onFocus : function()
22691     {
22692         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22693         this.show();
22694     },
22695     
22696     onBlur : function()
22697     {
22698         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22699         this.hide();
22700     },
22701     
22702     show : function()
22703     {
22704         this.picker().show();
22705         this.pop.show();
22706         this.update();
22707         this.place();
22708         
22709         this.fireEvent('show', this, this.date);
22710     },
22711     
22712     hide : function()
22713     {
22714         this.picker().hide();
22715         this.pop.hide();
22716         
22717         this.fireEvent('hide', this, this.date);
22718     },
22719     
22720     setTime : function()
22721     {
22722         this.hide();
22723         this.setValue(this.time.format(this.format));
22724         
22725         this.fireEvent('select', this, this.date);
22726         
22727         
22728     },
22729     
22730     onMousedown: function(e){
22731         e.stopPropagation();
22732         e.preventDefault();
22733     },
22734     
22735     onIncrementHours: function()
22736     {
22737         Roo.log('onIncrementHours');
22738         this.time = this.time.add(Date.HOUR, 1);
22739         this.update();
22740         
22741     },
22742     
22743     onDecrementHours: function()
22744     {
22745         Roo.log('onDecrementHours');
22746         this.time = this.time.add(Date.HOUR, -1);
22747         this.update();
22748     },
22749     
22750     onIncrementMinutes: function()
22751     {
22752         Roo.log('onIncrementMinutes');
22753         this.time = this.time.add(Date.MINUTE, 1);
22754         this.update();
22755     },
22756     
22757     onDecrementMinutes: function()
22758     {
22759         Roo.log('onDecrementMinutes');
22760         this.time = this.time.add(Date.MINUTE, -1);
22761         this.update();
22762     },
22763     
22764     onTogglePeriod: function()
22765     {
22766         Roo.log('onTogglePeriod');
22767         this.time = this.time.add(Date.HOUR, 12);
22768         this.update();
22769     }
22770     
22771    
22772 });
22773  
22774
22775 Roo.apply(Roo.bootstrap.TimeField,  {
22776   
22777     template : {
22778         tag: 'div',
22779         cls: 'datepicker dropdown-menu',
22780         cn: [
22781             {
22782                 tag: 'div',
22783                 cls: 'datepicker-time',
22784                 cn: [
22785                 {
22786                     tag: 'table',
22787                     cls: 'table-condensed',
22788                     cn:[
22789                         {
22790                             tag: 'tbody',
22791                             cn: [
22792                                 {
22793                                     tag: 'tr',
22794                                     cn: [
22795                                     {
22796                                         tag: 'td',
22797                                         colspan: '7'
22798                                     }
22799                                     ]
22800                                 }
22801                             ]
22802                         },
22803                         {
22804                             tag: 'tfoot',
22805                             cn: [
22806                                 {
22807                                     tag: 'tr',
22808                                     cn: [
22809                                     {
22810                                         tag: 'th',
22811                                         colspan: '7',
22812                                         cls: '',
22813                                         cn: [
22814                                             {
22815                                                 tag: 'button',
22816                                                 cls: 'btn btn-info ok',
22817                                                 html: 'OK'
22818                                             }
22819                                         ]
22820                                     }
22821                     
22822                                     ]
22823                                 }
22824                             ]
22825                         }
22826                     ]
22827                 }
22828                 ]
22829             }
22830         ]
22831     }
22832 });
22833
22834  
22835
22836  /*
22837  * - LGPL
22838  *
22839  * MonthField
22840  * 
22841  */
22842
22843 /**
22844  * @class Roo.bootstrap.MonthField
22845  * @extends Roo.bootstrap.Input
22846  * Bootstrap MonthField class
22847  * 
22848  * @cfg {String} language default en
22849  * 
22850  * @constructor
22851  * Create a new MonthField
22852  * @param {Object} config The config object
22853  */
22854
22855 Roo.bootstrap.MonthField = function(config){
22856     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22857     
22858     this.addEvents({
22859         /**
22860          * @event show
22861          * Fires when this field show.
22862          * @param {Roo.bootstrap.MonthField} this
22863          * @param {Mixed} date The date value
22864          */
22865         show : true,
22866         /**
22867          * @event show
22868          * Fires when this field hide.
22869          * @param {Roo.bootstrap.MonthField} this
22870          * @param {Mixed} date The date value
22871          */
22872         hide : true,
22873         /**
22874          * @event select
22875          * Fires when select a date.
22876          * @param {Roo.bootstrap.MonthField} this
22877          * @param {String} oldvalue The old value
22878          * @param {String} newvalue The new value
22879          */
22880         select : true
22881     });
22882 };
22883
22884 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22885     
22886     onRender: function(ct, position)
22887     {
22888         
22889         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22890         
22891         this.language = this.language || 'en';
22892         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22893         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22894         
22895         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22896         this.isInline = false;
22897         this.isInput = true;
22898         this.component = this.el.select('.add-on', true).first() || false;
22899         this.component = (this.component && this.component.length === 0) ? false : this.component;
22900         this.hasInput = this.component && this.inputEL().length;
22901         
22902         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22903         
22904         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22905         
22906         this.picker().on('mousedown', this.onMousedown, this);
22907         this.picker().on('click', this.onClick, this);
22908         
22909         this.picker().addClass('datepicker-dropdown');
22910         
22911         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22912             v.setStyle('width', '189px');
22913         });
22914         
22915         this.fillMonths();
22916         
22917         this.update();
22918         
22919         if(this.isInline) {
22920             this.show();
22921         }
22922         
22923     },
22924     
22925     setValue: function(v, suppressEvent)
22926     {   
22927         var o = this.getValue();
22928         
22929         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22930         
22931         this.update();
22932
22933         if(suppressEvent !== true){
22934             this.fireEvent('select', this, o, v);
22935         }
22936         
22937     },
22938     
22939     getValue: function()
22940     {
22941         return this.value;
22942     },
22943     
22944     onClick: function(e) 
22945     {
22946         e.stopPropagation();
22947         e.preventDefault();
22948         
22949         var target = e.getTarget();
22950         
22951         if(target.nodeName.toLowerCase() === 'i'){
22952             target = Roo.get(target).dom.parentNode;
22953         }
22954         
22955         var nodeName = target.nodeName;
22956         var className = target.className;
22957         var html = target.innerHTML;
22958         
22959         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22960             return;
22961         }
22962         
22963         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22964         
22965         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22966         
22967         this.hide();
22968                         
22969     },
22970     
22971     picker : function()
22972     {
22973         return this.pickerEl;
22974     },
22975     
22976     fillMonths: function()
22977     {    
22978         var i = 0;
22979         var months = this.picker().select('>.datepicker-months td', true).first();
22980         
22981         months.dom.innerHTML = '';
22982         
22983         while (i < 12) {
22984             var month = {
22985                 tag: 'span',
22986                 cls: 'month',
22987                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22988             };
22989             
22990             months.createChild(month);
22991         }
22992         
22993     },
22994     
22995     update: function()
22996     {
22997         var _this = this;
22998         
22999         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23000             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23001         }
23002         
23003         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23004             e.removeClass('active');
23005             
23006             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23007                 e.addClass('active');
23008             }
23009         })
23010     },
23011     
23012     place: function()
23013     {
23014         if(this.isInline) {
23015             return;
23016         }
23017         
23018         this.picker().removeClass(['bottom', 'top']);
23019         
23020         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23021             /*
23022              * place to the top of element!
23023              *
23024              */
23025             
23026             this.picker().addClass('top');
23027             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23028             
23029             return;
23030         }
23031         
23032         this.picker().addClass('bottom');
23033         
23034         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23035     },
23036     
23037     onFocus : function()
23038     {
23039         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23040         this.show();
23041     },
23042     
23043     onBlur : function()
23044     {
23045         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23046         
23047         var d = this.inputEl().getValue();
23048         
23049         this.setValue(d);
23050                 
23051         this.hide();
23052     },
23053     
23054     show : function()
23055     {
23056         this.picker().show();
23057         this.picker().select('>.datepicker-months', true).first().show();
23058         this.update();
23059         this.place();
23060         
23061         this.fireEvent('show', this, this.date);
23062     },
23063     
23064     hide : function()
23065     {
23066         if(this.isInline) {
23067             return;
23068         }
23069         this.picker().hide();
23070         this.fireEvent('hide', this, this.date);
23071         
23072     },
23073     
23074     onMousedown: function(e)
23075     {
23076         e.stopPropagation();
23077         e.preventDefault();
23078     },
23079     
23080     keyup: function(e)
23081     {
23082         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23083         this.update();
23084     },
23085
23086     fireKey: function(e)
23087     {
23088         if (!this.picker().isVisible()){
23089             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23090                 this.show();
23091             }
23092             return;
23093         }
23094         
23095         var dir;
23096         
23097         switch(e.keyCode){
23098             case 27: // escape
23099                 this.hide();
23100                 e.preventDefault();
23101                 break;
23102             case 37: // left
23103             case 39: // right
23104                 dir = e.keyCode == 37 ? -1 : 1;
23105                 
23106                 this.vIndex = this.vIndex + dir;
23107                 
23108                 if(this.vIndex < 0){
23109                     this.vIndex = 0;
23110                 }
23111                 
23112                 if(this.vIndex > 11){
23113                     this.vIndex = 11;
23114                 }
23115                 
23116                 if(isNaN(this.vIndex)){
23117                     this.vIndex = 0;
23118                 }
23119                 
23120                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23121                 
23122                 break;
23123             case 38: // up
23124             case 40: // down
23125                 
23126                 dir = e.keyCode == 38 ? -1 : 1;
23127                 
23128                 this.vIndex = this.vIndex + dir * 4;
23129                 
23130                 if(this.vIndex < 0){
23131                     this.vIndex = 0;
23132                 }
23133                 
23134                 if(this.vIndex > 11){
23135                     this.vIndex = 11;
23136                 }
23137                 
23138                 if(isNaN(this.vIndex)){
23139                     this.vIndex = 0;
23140                 }
23141                 
23142                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23143                 break;
23144                 
23145             case 13: // enter
23146                 
23147                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23148                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23149                 }
23150                 
23151                 this.hide();
23152                 e.preventDefault();
23153                 break;
23154             case 9: // tab
23155                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23156                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23157                 }
23158                 this.hide();
23159                 break;
23160             case 16: // shift
23161             case 17: // ctrl
23162             case 18: // alt
23163                 break;
23164             default :
23165                 this.hide();
23166                 
23167         }
23168     },
23169     
23170     remove: function() 
23171     {
23172         this.picker().remove();
23173     }
23174    
23175 });
23176
23177 Roo.apply(Roo.bootstrap.MonthField,  {
23178     
23179     content : {
23180         tag: 'tbody',
23181         cn: [
23182         {
23183             tag: 'tr',
23184             cn: [
23185             {
23186                 tag: 'td',
23187                 colspan: '7'
23188             }
23189             ]
23190         }
23191         ]
23192     },
23193     
23194     dates:{
23195         en: {
23196             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23197             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23198         }
23199     }
23200 });
23201
23202 Roo.apply(Roo.bootstrap.MonthField,  {
23203   
23204     template : {
23205         tag: 'div',
23206         cls: 'datepicker dropdown-menu roo-dynamic',
23207         cn: [
23208             {
23209                 tag: 'div',
23210                 cls: 'datepicker-months',
23211                 cn: [
23212                 {
23213                     tag: 'table',
23214                     cls: 'table-condensed',
23215                     cn:[
23216                         Roo.bootstrap.DateField.content
23217                     ]
23218                 }
23219                 ]
23220             }
23221         ]
23222     }
23223 });
23224
23225  
23226
23227  
23228  /*
23229  * - LGPL
23230  *
23231  * CheckBox
23232  * 
23233  */
23234
23235 /**
23236  * @class Roo.bootstrap.CheckBox
23237  * @extends Roo.bootstrap.Input
23238  * Bootstrap CheckBox class
23239  * 
23240  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23241  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23242  * @cfg {String} boxLabel The text that appears beside the checkbox
23243  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23244  * @cfg {Boolean} checked initnal the element
23245  * @cfg {Boolean} inline inline the element (default false)
23246  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23247  * @cfg {String} tooltip label tooltip
23248  * 
23249  * @constructor
23250  * Create a new CheckBox
23251  * @param {Object} config The config object
23252  */
23253
23254 Roo.bootstrap.CheckBox = function(config){
23255     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23256    
23257     this.addEvents({
23258         /**
23259         * @event check
23260         * Fires when the element is checked or unchecked.
23261         * @param {Roo.bootstrap.CheckBox} this This input
23262         * @param {Boolean} checked The new checked value
23263         */
23264        check : true,
23265        /**
23266         * @event click
23267         * Fires when the element is click.
23268         * @param {Roo.bootstrap.CheckBox} this This input
23269         */
23270        click : true
23271     });
23272     
23273 };
23274
23275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23276   
23277     inputType: 'checkbox',
23278     inputValue: 1,
23279     valueOff: 0,
23280     boxLabel: false,
23281     checked: false,
23282     weight : false,
23283     inline: false,
23284     tooltip : '',
23285     
23286     // checkbox success does not make any sense really.. 
23287     invalidClass : "",
23288     validClass : "",
23289     
23290     
23291     getAutoCreate : function()
23292     {
23293         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23294         
23295         var id = Roo.id();
23296         
23297         var cfg = {};
23298         
23299         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23300         
23301         if(this.inline){
23302             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23303         }
23304         
23305         var input =  {
23306             tag: 'input',
23307             id : id,
23308             type : this.inputType,
23309             value : this.inputValue,
23310             cls : 'roo-' + this.inputType, //'form-box',
23311             placeholder : this.placeholder || ''
23312             
23313         };
23314         
23315         if(this.inputType != 'radio'){
23316             var hidden =  {
23317                 tag: 'input',
23318                 type : 'hidden',
23319                 cls : 'roo-hidden-value',
23320                 value : this.checked ? this.inputValue : this.valueOff
23321             };
23322         }
23323         
23324             
23325         if (this.weight) { // Validity check?
23326             cfg.cls += " " + this.inputType + "-" + this.weight;
23327         }
23328         
23329         if (this.disabled) {
23330             input.disabled=true;
23331         }
23332         
23333         if(this.checked){
23334             input.checked = this.checked;
23335         }
23336         
23337         if (this.name) {
23338             
23339             input.name = this.name;
23340             
23341             if(this.inputType != 'radio'){
23342                 hidden.name = this.name;
23343                 input.name = '_hidden_' + this.name;
23344             }
23345         }
23346         
23347         if (this.size) {
23348             input.cls += ' input-' + this.size;
23349         }
23350         
23351         var settings=this;
23352         
23353         ['xs','sm','md','lg'].map(function(size){
23354             if (settings[size]) {
23355                 cfg.cls += ' col-' + size + '-' + settings[size];
23356             }
23357         });
23358         
23359         var inputblock = input;
23360          
23361         if (this.before || this.after) {
23362             
23363             inputblock = {
23364                 cls : 'input-group',
23365                 cn :  [] 
23366             };
23367             
23368             if (this.before) {
23369                 inputblock.cn.push({
23370                     tag :'span',
23371                     cls : 'input-group-addon',
23372                     html : this.before
23373                 });
23374             }
23375             
23376             inputblock.cn.push(input);
23377             
23378             if(this.inputType != 'radio'){
23379                 inputblock.cn.push(hidden);
23380             }
23381             
23382             if (this.after) {
23383                 inputblock.cn.push({
23384                     tag :'span',
23385                     cls : 'input-group-addon',
23386                     html : this.after
23387                 });
23388             }
23389             
23390         }
23391         var boxLabelCfg = false;
23392         
23393         if(this.boxLabel){
23394            
23395             boxLabelCfg = {
23396                 tag: 'label',
23397                 //'for': id, // box label is handled by onclick - so no for...
23398                 cls: 'box-label',
23399                 html: this.boxLabel
23400             };
23401             if(this.tooltip){
23402                 boxLabelCfg.tooltip = this.tooltip;
23403             }
23404              
23405         }
23406         
23407         
23408         if (align ==='left' && this.fieldLabel.length) {
23409 //                Roo.log("left and has label");
23410             cfg.cn = [
23411                 {
23412                     tag: 'label',
23413                     'for' :  id,
23414                     cls : 'control-label',
23415                     html : this.fieldLabel
23416                 },
23417                 {
23418                     cls : "", 
23419                     cn: [
23420                         inputblock
23421                     ]
23422                 }
23423             ];
23424             
23425             if (boxLabelCfg) {
23426                 cfg.cn[1].cn.push(boxLabelCfg);
23427             }
23428             
23429             if(this.labelWidth > 12){
23430                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23431             }
23432             
23433             if(this.labelWidth < 13 && this.labelmd == 0){
23434                 this.labelmd = this.labelWidth;
23435             }
23436             
23437             if(this.labellg > 0){
23438                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23439                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23440             }
23441             
23442             if(this.labelmd > 0){
23443                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23444                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23445             }
23446             
23447             if(this.labelsm > 0){
23448                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23449                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23450             }
23451             
23452             if(this.labelxs > 0){
23453                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23454                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23455             }
23456             
23457         } else if ( this.fieldLabel.length) {
23458 //                Roo.log(" label");
23459                 cfg.cn = [
23460                    
23461                     {
23462                         tag: this.boxLabel ? 'span' : 'label',
23463                         'for': id,
23464                         cls: 'control-label box-input-label',
23465                         //cls : 'input-group-addon',
23466                         html : this.fieldLabel
23467                     },
23468                     
23469                     inputblock
23470                     
23471                 ];
23472                 if (boxLabelCfg) {
23473                     cfg.cn.push(boxLabelCfg);
23474                 }
23475
23476         } else {
23477             
23478 //                Roo.log(" no label && no align");
23479                 cfg.cn = [  inputblock ] ;
23480                 if (boxLabelCfg) {
23481                     cfg.cn.push(boxLabelCfg);
23482                 }
23483
23484                 
23485         }
23486         
23487        
23488         
23489         if(this.inputType != 'radio'){
23490             cfg.cn.push(hidden);
23491         }
23492         
23493         return cfg;
23494         
23495     },
23496     
23497     /**
23498      * return the real input element.
23499      */
23500     inputEl: function ()
23501     {
23502         return this.el.select('input.roo-' + this.inputType,true).first();
23503     },
23504     hiddenEl: function ()
23505     {
23506         return this.el.select('input.roo-hidden-value',true).first();
23507     },
23508     
23509     labelEl: function()
23510     {
23511         return this.el.select('label.control-label',true).first();
23512     },
23513     /* depricated... */
23514     
23515     label: function()
23516     {
23517         return this.labelEl();
23518     },
23519     
23520     boxLabelEl: function()
23521     {
23522         return this.el.select('label.box-label',true).first();
23523     },
23524     
23525     initEvents : function()
23526     {
23527 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23528         
23529         this.inputEl().on('click', this.onClick,  this);
23530         
23531         if (this.boxLabel) { 
23532             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23533         }
23534         
23535         this.startValue = this.getValue();
23536         
23537         if(this.groupId){
23538             Roo.bootstrap.CheckBox.register(this);
23539         }
23540     },
23541     
23542     onClick : function(e)
23543     {   
23544         if(this.fireEvent('click', this, e) !== false){
23545             this.setChecked(!this.checked);
23546         }
23547         
23548     },
23549     
23550     setChecked : function(state,suppressEvent)
23551     {
23552         this.startValue = this.getValue();
23553
23554         if(this.inputType == 'radio'){
23555             
23556             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557                 e.dom.checked = false;
23558             });
23559             
23560             this.inputEl().dom.checked = true;
23561             
23562             this.inputEl().dom.value = this.inputValue;
23563             
23564             if(suppressEvent !== true){
23565                 this.fireEvent('check', this, true);
23566             }
23567             
23568             this.validate();
23569             
23570             return;
23571         }
23572         
23573         this.checked = state;
23574         
23575         this.inputEl().dom.checked = state;
23576         
23577         
23578         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23579         
23580         if(suppressEvent !== true){
23581             this.fireEvent('check', this, state);
23582         }
23583         
23584         this.validate();
23585     },
23586     
23587     getValue : function()
23588     {
23589         if(this.inputType == 'radio'){
23590             return this.getGroupValue();
23591         }
23592         
23593         return this.hiddenEl().dom.value;
23594         
23595     },
23596     
23597     getGroupValue : function()
23598     {
23599         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23600             return '';
23601         }
23602         
23603         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23604     },
23605     
23606     setValue : function(v,suppressEvent)
23607     {
23608         if(this.inputType == 'radio'){
23609             this.setGroupValue(v, suppressEvent);
23610             return;
23611         }
23612         
23613         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23614         
23615         this.validate();
23616     },
23617     
23618     setGroupValue : function(v, suppressEvent)
23619     {
23620         this.startValue = this.getValue();
23621         
23622         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623             e.dom.checked = false;
23624             
23625             if(e.dom.value == v){
23626                 e.dom.checked = true;
23627             }
23628         });
23629         
23630         if(suppressEvent !== true){
23631             this.fireEvent('check', this, true);
23632         }
23633
23634         this.validate();
23635         
23636         return;
23637     },
23638     
23639     validate : function()
23640     {
23641         if(this.getVisibilityEl().hasClass('hidden')){
23642             return true;
23643         }
23644         
23645         if(
23646                 this.disabled || 
23647                 (this.inputType == 'radio' && this.validateRadio()) ||
23648                 (this.inputType == 'checkbox' && this.validateCheckbox())
23649         ){
23650             this.markValid();
23651             return true;
23652         }
23653         
23654         this.markInvalid();
23655         return false;
23656     },
23657     
23658     validateRadio : function()
23659     {
23660         if(this.getVisibilityEl().hasClass('hidden')){
23661             return true;
23662         }
23663         
23664         if(this.allowBlank){
23665             return true;
23666         }
23667         
23668         var valid = false;
23669         
23670         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671             if(!e.dom.checked){
23672                 return;
23673             }
23674             
23675             valid = true;
23676             
23677             return false;
23678         });
23679         
23680         return valid;
23681     },
23682     
23683     validateCheckbox : function()
23684     {
23685         if(!this.groupId){
23686             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23687             //return (this.getValue() == this.inputValue) ? true : false;
23688         }
23689         
23690         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23691         
23692         if(!group){
23693             return false;
23694         }
23695         
23696         var r = false;
23697         
23698         for(var i in group){
23699             if(group[i].el.isVisible(true)){
23700                 r = false;
23701                 break;
23702             }
23703             
23704             r = true;
23705         }
23706         
23707         for(var i in group){
23708             if(r){
23709                 break;
23710             }
23711             
23712             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23713         }
23714         
23715         return r;
23716     },
23717     
23718     /**
23719      * Mark this field as valid
23720      */
23721     markValid : function()
23722     {
23723         var _this = this;
23724         
23725         this.fireEvent('valid', this);
23726         
23727         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23728         
23729         if(this.groupId){
23730             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23731         }
23732         
23733         if(label){
23734             label.markValid();
23735         }
23736
23737         if(this.inputType == 'radio'){
23738             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23739                 var fg = e.findParent('.form-group', false, true);
23740                 if (Roo.bootstrap.version == 3) {
23741                     fg.removeClass([_this.invalidClass, _this.validClass]);
23742                     fg.addClass(_this.validClass);
23743                 } else {
23744                     fg.removeClass(['is-valid', 'is-invalid']);
23745                     fg.addClass('is-valid');
23746                 }
23747             });
23748             
23749             return;
23750         }
23751
23752         if(!this.groupId){
23753             var fg = this.el.findParent('.form-group', false, true);
23754             if (Roo.bootstrap.version == 3) {
23755                 fg.removeClass([this.invalidClass, this.validClass]);
23756                 fg.addClass(this.validClass);
23757             } else {
23758                 fg.removeClass(['is-valid', 'is-invalid']);
23759                 fg.addClass('is-valid');
23760             }
23761             return;
23762         }
23763         
23764         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23765         
23766         if(!group){
23767             return;
23768         }
23769         
23770         for(var i in group){
23771             var fg = group[i].el.findParent('.form-group', false, true);
23772             if (Roo.bootstrap.version == 3) {
23773                 fg.removeClass([this.invalidClass, this.validClass]);
23774                 fg.addClass(this.validClass);
23775             } else {
23776                 fg.removeClass(['is-valid', 'is-invalid']);
23777                 fg.addClass('is-valid');
23778             }
23779         }
23780     },
23781     
23782      /**
23783      * Mark this field as invalid
23784      * @param {String} msg The validation message
23785      */
23786     markInvalid : function(msg)
23787     {
23788         if(this.allowBlank){
23789             return;
23790         }
23791         
23792         var _this = this;
23793         
23794         this.fireEvent('invalid', this, msg);
23795         
23796         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23797         
23798         if(this.groupId){
23799             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23800         }
23801         
23802         if(label){
23803             label.markInvalid();
23804         }
23805             
23806         if(this.inputType == 'radio'){
23807             
23808             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23809                 var fg = e.findParent('.form-group', false, true);
23810                 if (Roo.bootstrap.version == 3) {
23811                     fg.removeClass([_this.invalidClass, _this.validClass]);
23812                     fg.addClass(_this.invalidClass);
23813                 } else {
23814                     fg.removeClass(['is-invalid', 'is-valid']);
23815                     fg.addClass('is-invalid');
23816                 }
23817             });
23818             
23819             return;
23820         }
23821         
23822         if(!this.groupId){
23823             var fg = this.el.findParent('.form-group', false, true);
23824             if (Roo.bootstrap.version == 3) {
23825                 fg.removeClass([_this.invalidClass, _this.validClass]);
23826                 fg.addClass(_this.invalidClass);
23827             } else {
23828                 fg.removeClass(['is-invalid', 'is-valid']);
23829                 fg.addClass('is-invalid');
23830             }
23831             return;
23832         }
23833         
23834         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23835         
23836         if(!group){
23837             return;
23838         }
23839         
23840         for(var i in group){
23841             var fg = group[i].el.findParent('.form-group', false, true);
23842             if (Roo.bootstrap.version == 3) {
23843                 fg.removeClass([_this.invalidClass, _this.validClass]);
23844                 fg.addClass(_this.invalidClass);
23845             } else {
23846                 fg.removeClass(['is-invalid', 'is-valid']);
23847                 fg.addClass('is-invalid');
23848             }
23849         }
23850         
23851     },
23852     
23853     clearInvalid : function()
23854     {
23855         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23856         
23857         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23858         
23859         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23860         
23861         if (label && label.iconEl) {
23862             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23863             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23864         }
23865     },
23866     
23867     disable : function()
23868     {
23869         if(this.inputType != 'radio'){
23870             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23871             return;
23872         }
23873         
23874         var _this = this;
23875         
23876         if(this.rendered){
23877             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878                 _this.getActionEl().addClass(this.disabledClass);
23879                 e.dom.disabled = true;
23880             });
23881         }
23882         
23883         this.disabled = true;
23884         this.fireEvent("disable", this);
23885         return this;
23886     },
23887
23888     enable : function()
23889     {
23890         if(this.inputType != 'radio'){
23891             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23892             return;
23893         }
23894         
23895         var _this = this;
23896         
23897         if(this.rendered){
23898             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23899                 _this.getActionEl().removeClass(this.disabledClass);
23900                 e.dom.disabled = false;
23901             });
23902         }
23903         
23904         this.disabled = false;
23905         this.fireEvent("enable", this);
23906         return this;
23907     },
23908     
23909     setBoxLabel : function(v)
23910     {
23911         this.boxLabel = v;
23912         
23913         if(this.rendered){
23914             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23915         }
23916     }
23917
23918 });
23919
23920 Roo.apply(Roo.bootstrap.CheckBox, {
23921     
23922     groups: {},
23923     
23924      /**
23925     * register a CheckBox Group
23926     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23927     */
23928     register : function(checkbox)
23929     {
23930         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23931             this.groups[checkbox.groupId] = {};
23932         }
23933         
23934         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23935             return;
23936         }
23937         
23938         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23939         
23940     },
23941     /**
23942     * fetch a CheckBox Group based on the group ID
23943     * @param {string} the group ID
23944     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23945     */
23946     get: function(groupId) {
23947         if (typeof(this.groups[groupId]) == 'undefined') {
23948             return false;
23949         }
23950         
23951         return this.groups[groupId] ;
23952     }
23953     
23954     
23955 });
23956 /*
23957  * - LGPL
23958  *
23959  * RadioItem
23960  * 
23961  */
23962
23963 /**
23964  * @class Roo.bootstrap.Radio
23965  * @extends Roo.bootstrap.Component
23966  * Bootstrap Radio class
23967  * @cfg {String} boxLabel - the label associated
23968  * @cfg {String} value - the value of radio
23969  * 
23970  * @constructor
23971  * Create a new Radio
23972  * @param {Object} config The config object
23973  */
23974 Roo.bootstrap.Radio = function(config){
23975     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23976     
23977 };
23978
23979 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23980     
23981     boxLabel : '',
23982     
23983     value : '',
23984     
23985     getAutoCreate : function()
23986     {
23987         var cfg = {
23988             tag : 'div',
23989             cls : 'form-group radio',
23990             cn : [
23991                 {
23992                     tag : 'label',
23993                     cls : 'box-label',
23994                     html : this.boxLabel
23995                 }
23996             ]
23997         };
23998         
23999         return cfg;
24000     },
24001     
24002     initEvents : function() 
24003     {
24004         this.parent().register(this);
24005         
24006         this.el.on('click', this.onClick, this);
24007         
24008     },
24009     
24010     onClick : function(e)
24011     {
24012         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24013             this.setChecked(true);
24014         }
24015     },
24016     
24017     setChecked : function(state, suppressEvent)
24018     {
24019         this.parent().setValue(this.value, suppressEvent);
24020         
24021     },
24022     
24023     setBoxLabel : function(v)
24024     {
24025         this.boxLabel = v;
24026         
24027         if(this.rendered){
24028             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24029         }
24030     }
24031     
24032 });
24033  
24034
24035  /*
24036  * - LGPL
24037  *
24038  * Input
24039  * 
24040  */
24041
24042 /**
24043  * @class Roo.bootstrap.SecurePass
24044  * @extends Roo.bootstrap.Input
24045  * Bootstrap SecurePass class
24046  *
24047  * 
24048  * @constructor
24049  * Create a new SecurePass
24050  * @param {Object} config The config object
24051  */
24052  
24053 Roo.bootstrap.SecurePass = function (config) {
24054     // these go here, so the translation tool can replace them..
24055     this.errors = {
24056         PwdEmpty: "Please type a password, and then retype it to confirm.",
24057         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24058         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24059         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24060         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24061         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24062         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24063         TooWeak: "Your password is Too Weak."
24064     },
24065     this.meterLabel = "Password strength:";
24066     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24067     this.meterClass = [
24068         "roo-password-meter-tooweak", 
24069         "roo-password-meter-weak", 
24070         "roo-password-meter-medium", 
24071         "roo-password-meter-strong", 
24072         "roo-password-meter-grey"
24073     ];
24074     
24075     this.errors = {};
24076     
24077     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24078 }
24079
24080 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24081     /**
24082      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24083      * {
24084      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24085      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24086      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24087      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24088      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24089      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24090      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24091      * })
24092      */
24093     // private
24094     
24095     meterWidth: 300,
24096     errorMsg :'',    
24097     errors: false,
24098     imageRoot: '/',
24099     /**
24100      * @cfg {String/Object} Label for the strength meter (defaults to
24101      * 'Password strength:')
24102      */
24103     // private
24104     meterLabel: '',
24105     /**
24106      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24107      * ['Weak', 'Medium', 'Strong'])
24108      */
24109     // private    
24110     pwdStrengths: false,    
24111     // private
24112     strength: 0,
24113     // private
24114     _lastPwd: null,
24115     // private
24116     kCapitalLetter: 0,
24117     kSmallLetter: 1,
24118     kDigit: 2,
24119     kPunctuation: 3,
24120     
24121     insecure: false,
24122     // private
24123     initEvents: function ()
24124     {
24125         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24126
24127         if (this.el.is('input[type=password]') && Roo.isSafari) {
24128             this.el.on('keydown', this.SafariOnKeyDown, this);
24129         }
24130
24131         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24132     },
24133     // private
24134     onRender: function (ct, position)
24135     {
24136         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24137         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24138         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24139
24140         this.trigger.createChild({
24141                    cn: [
24142                     {
24143                     //id: 'PwdMeter',
24144                     tag: 'div',
24145                     cls: 'roo-password-meter-grey col-xs-12',
24146                     style: {
24147                         //width: 0,
24148                         //width: this.meterWidth + 'px'                                                
24149                         }
24150                     },
24151                     {                            
24152                          cls: 'roo-password-meter-text'                          
24153                     }
24154                 ]            
24155         });
24156
24157          
24158         if (this.hideTrigger) {
24159             this.trigger.setDisplayed(false);
24160         }
24161         this.setSize(this.width || '', this.height || '');
24162     },
24163     // private
24164     onDestroy: function ()
24165     {
24166         if (this.trigger) {
24167             this.trigger.removeAllListeners();
24168             this.trigger.remove();
24169         }
24170         if (this.wrap) {
24171             this.wrap.remove();
24172         }
24173         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24174     },
24175     // private
24176     checkStrength: function ()
24177     {
24178         var pwd = this.inputEl().getValue();
24179         if (pwd == this._lastPwd) {
24180             return;
24181         }
24182
24183         var strength;
24184         if (this.ClientSideStrongPassword(pwd)) {
24185             strength = 3;
24186         } else if (this.ClientSideMediumPassword(pwd)) {
24187             strength = 2;
24188         } else if (this.ClientSideWeakPassword(pwd)) {
24189             strength = 1;
24190         } else {
24191             strength = 0;
24192         }
24193         
24194         Roo.log('strength1: ' + strength);
24195         
24196         //var pm = this.trigger.child('div/div/div').dom;
24197         var pm = this.trigger.child('div/div');
24198         pm.removeClass(this.meterClass);
24199         pm.addClass(this.meterClass[strength]);
24200                 
24201         
24202         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24203                 
24204         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24205         
24206         this._lastPwd = pwd;
24207     },
24208     reset: function ()
24209     {
24210         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24211         
24212         this._lastPwd = '';
24213         
24214         var pm = this.trigger.child('div/div');
24215         pm.removeClass(this.meterClass);
24216         pm.addClass('roo-password-meter-grey');        
24217         
24218         
24219         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24220         
24221         pt.innerHTML = '';
24222         this.inputEl().dom.type='password';
24223     },
24224     // private
24225     validateValue: function (value)
24226     {
24227         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24228             return false;
24229         }
24230         if (value.length == 0) {
24231             if (this.allowBlank) {
24232                 this.clearInvalid();
24233                 return true;
24234             }
24235
24236             this.markInvalid(this.errors.PwdEmpty);
24237             this.errorMsg = this.errors.PwdEmpty;
24238             return false;
24239         }
24240         
24241         if(this.insecure){
24242             return true;
24243         }
24244         
24245         if (!value.match(/[\x21-\x7e]+/)) {
24246             this.markInvalid(this.errors.PwdBadChar);
24247             this.errorMsg = this.errors.PwdBadChar;
24248             return false;
24249         }
24250         if (value.length < 6) {
24251             this.markInvalid(this.errors.PwdShort);
24252             this.errorMsg = this.errors.PwdShort;
24253             return false;
24254         }
24255         if (value.length > 16) {
24256             this.markInvalid(this.errors.PwdLong);
24257             this.errorMsg = this.errors.PwdLong;
24258             return false;
24259         }
24260         var strength;
24261         if (this.ClientSideStrongPassword(value)) {
24262             strength = 3;
24263         } else if (this.ClientSideMediumPassword(value)) {
24264             strength = 2;
24265         } else if (this.ClientSideWeakPassword(value)) {
24266             strength = 1;
24267         } else {
24268             strength = 0;
24269         }
24270
24271         
24272         if (strength < 2) {
24273             //this.markInvalid(this.errors.TooWeak);
24274             this.errorMsg = this.errors.TooWeak;
24275             //return false;
24276         }
24277         
24278         
24279         console.log('strength2: ' + strength);
24280         
24281         //var pm = this.trigger.child('div/div/div').dom;
24282         
24283         var pm = this.trigger.child('div/div');
24284         pm.removeClass(this.meterClass);
24285         pm.addClass(this.meterClass[strength]);
24286                 
24287         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24288                 
24289         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24290         
24291         this.errorMsg = ''; 
24292         return true;
24293     },
24294     // private
24295     CharacterSetChecks: function (type)
24296     {
24297         this.type = type;
24298         this.fResult = false;
24299     },
24300     // private
24301     isctype: function (character, type)
24302     {
24303         switch (type) {  
24304             case this.kCapitalLetter:
24305                 if (character >= 'A' && character <= 'Z') {
24306                     return true;
24307                 }
24308                 break;
24309             
24310             case this.kSmallLetter:
24311                 if (character >= 'a' && character <= 'z') {
24312                     return true;
24313                 }
24314                 break;
24315             
24316             case this.kDigit:
24317                 if (character >= '0' && character <= '9') {
24318                     return true;
24319                 }
24320                 break;
24321             
24322             case this.kPunctuation:
24323                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24324                     return true;
24325                 }
24326                 break;
24327             
24328             default:
24329                 return false;
24330         }
24331
24332     },
24333     // private
24334     IsLongEnough: function (pwd, size)
24335     {
24336         return !(pwd == null || isNaN(size) || pwd.length < size);
24337     },
24338     // private
24339     SpansEnoughCharacterSets: function (word, nb)
24340     {
24341         if (!this.IsLongEnough(word, nb))
24342         {
24343             return false;
24344         }
24345
24346         var characterSetChecks = new Array(
24347             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24348             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24349         );
24350         
24351         for (var index = 0; index < word.length; ++index) {
24352             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24353                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24354                     characterSetChecks[nCharSet].fResult = true;
24355                     break;
24356                 }
24357             }
24358         }
24359
24360         var nCharSets = 0;
24361         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362             if (characterSetChecks[nCharSet].fResult) {
24363                 ++nCharSets;
24364             }
24365         }
24366
24367         if (nCharSets < nb) {
24368             return false;
24369         }
24370         return true;
24371     },
24372     // private
24373     ClientSideStrongPassword: function (pwd)
24374     {
24375         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24376     },
24377     // private
24378     ClientSideMediumPassword: function (pwd)
24379     {
24380         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24381     },
24382     // private
24383     ClientSideWeakPassword: function (pwd)
24384     {
24385         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24386     }
24387           
24388 })//<script type="text/javascript">
24389
24390 /*
24391  * Based  Ext JS Library 1.1.1
24392  * Copyright(c) 2006-2007, Ext JS, LLC.
24393  * LGPL
24394  *
24395  */
24396  
24397 /**
24398  * @class Roo.HtmlEditorCore
24399  * @extends Roo.Component
24400  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24401  *
24402  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24403  */
24404
24405 Roo.HtmlEditorCore = function(config){
24406     
24407     
24408     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24409     
24410     
24411     this.addEvents({
24412         /**
24413          * @event initialize
24414          * Fires when the editor is fully initialized (including the iframe)
24415          * @param {Roo.HtmlEditorCore} this
24416          */
24417         initialize: true,
24418         /**
24419          * @event activate
24420          * Fires when the editor is first receives the focus. Any insertion must wait
24421          * until after this event.
24422          * @param {Roo.HtmlEditorCore} this
24423          */
24424         activate: true,
24425          /**
24426          * @event beforesync
24427          * Fires before the textarea is updated with content from the editor iframe. Return false
24428          * to cancel the sync.
24429          * @param {Roo.HtmlEditorCore} this
24430          * @param {String} html
24431          */
24432         beforesync: true,
24433          /**
24434          * @event beforepush
24435          * Fires before the iframe editor is updated with content from the textarea. Return false
24436          * to cancel the push.
24437          * @param {Roo.HtmlEditorCore} this
24438          * @param {String} html
24439          */
24440         beforepush: true,
24441          /**
24442          * @event sync
24443          * Fires when the textarea is updated with content from the editor iframe.
24444          * @param {Roo.HtmlEditorCore} this
24445          * @param {String} html
24446          */
24447         sync: true,
24448          /**
24449          * @event push
24450          * Fires when the iframe editor is updated with content from the textarea.
24451          * @param {Roo.HtmlEditorCore} this
24452          * @param {String} html
24453          */
24454         push: true,
24455         
24456         /**
24457          * @event editorevent
24458          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24459          * @param {Roo.HtmlEditorCore} this
24460          */
24461         editorevent: true
24462         
24463     });
24464     
24465     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24466     
24467     // defaults : white / black...
24468     this.applyBlacklists();
24469     
24470     
24471     
24472 };
24473
24474
24475 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24476
24477
24478      /**
24479      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24480      */
24481     
24482     owner : false,
24483     
24484      /**
24485      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24486      *                        Roo.resizable.
24487      */
24488     resizable : false,
24489      /**
24490      * @cfg {Number} height (in pixels)
24491      */   
24492     height: 300,
24493    /**
24494      * @cfg {Number} width (in pixels)
24495      */   
24496     width: 500,
24497     
24498     /**
24499      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24500      * 
24501      */
24502     stylesheets: false,
24503     
24504     // id of frame..
24505     frameId: false,
24506     
24507     // private properties
24508     validationEvent : false,
24509     deferHeight: true,
24510     initialized : false,
24511     activated : false,
24512     sourceEditMode : false,
24513     onFocus : Roo.emptyFn,
24514     iframePad:3,
24515     hideMode:'offsets',
24516     
24517     clearUp: true,
24518     
24519     // blacklist + whitelisted elements..
24520     black: false,
24521     white: false,
24522      
24523     bodyCls : '',
24524
24525     /**
24526      * Protected method that will not generally be called directly. It
24527      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24528      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24529      */
24530     getDocMarkup : function(){
24531         // body styles..
24532         var st = '';
24533         
24534         // inherit styels from page...?? 
24535         if (this.stylesheets === false) {
24536             
24537             Roo.get(document.head).select('style').each(function(node) {
24538                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24539             });
24540             
24541             Roo.get(document.head).select('link').each(function(node) { 
24542                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24543             });
24544             
24545         } else if (!this.stylesheets.length) {
24546                 // simple..
24547                 st = '<style type="text/css">' +
24548                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24549                    '</style>';
24550         } else {
24551             for (var i in this.stylesheets) { 
24552                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24553             }
24554             
24555         }
24556         
24557         st +=  '<style type="text/css">' +
24558             'IMG { cursor: pointer } ' +
24559         '</style>';
24560
24561         var cls = 'roo-htmleditor-body';
24562         
24563         if(this.bodyCls.length){
24564             cls += ' ' + this.bodyCls;
24565         }
24566         
24567         return '<html><head>' + st  +
24568             //<style type="text/css">' +
24569             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24570             //'</style>' +
24571             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24572     },
24573
24574     // private
24575     onRender : function(ct, position)
24576     {
24577         var _t = this;
24578         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24579         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24580         
24581         
24582         this.el.dom.style.border = '0 none';
24583         this.el.dom.setAttribute('tabIndex', -1);
24584         this.el.addClass('x-hidden hide');
24585         
24586         
24587         
24588         if(Roo.isIE){ // fix IE 1px bogus margin
24589             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24590         }
24591        
24592         
24593         this.frameId = Roo.id();
24594         
24595          
24596         
24597         var iframe = this.owner.wrap.createChild({
24598             tag: 'iframe',
24599             cls: 'form-control', // bootstrap..
24600             id: this.frameId,
24601             name: this.frameId,
24602             frameBorder : 'no',
24603             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24604         }, this.el
24605         );
24606         
24607         
24608         this.iframe = iframe.dom;
24609
24610          this.assignDocWin();
24611         
24612         this.doc.designMode = 'on';
24613        
24614         this.doc.open();
24615         this.doc.write(this.getDocMarkup());
24616         this.doc.close();
24617
24618         
24619         var task = { // must defer to wait for browser to be ready
24620             run : function(){
24621                 //console.log("run task?" + this.doc.readyState);
24622                 this.assignDocWin();
24623                 if(this.doc.body || this.doc.readyState == 'complete'){
24624                     try {
24625                         this.doc.designMode="on";
24626                     } catch (e) {
24627                         return;
24628                     }
24629                     Roo.TaskMgr.stop(task);
24630                     this.initEditor.defer(10, this);
24631                 }
24632             },
24633             interval : 10,
24634             duration: 10000,
24635             scope: this
24636         };
24637         Roo.TaskMgr.start(task);
24638
24639     },
24640
24641     // private
24642     onResize : function(w, h)
24643     {
24644          Roo.log('resize: ' +w + ',' + h );
24645         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24646         if(!this.iframe){
24647             return;
24648         }
24649         if(typeof w == 'number'){
24650             
24651             this.iframe.style.width = w + 'px';
24652         }
24653         if(typeof h == 'number'){
24654             
24655             this.iframe.style.height = h + 'px';
24656             if(this.doc){
24657                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24658             }
24659         }
24660         
24661     },
24662
24663     /**
24664      * Toggles the editor between standard and source edit mode.
24665      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24666      */
24667     toggleSourceEdit : function(sourceEditMode){
24668         
24669         this.sourceEditMode = sourceEditMode === true;
24670         
24671         if(this.sourceEditMode){
24672  
24673             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24674             
24675         }else{
24676             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24677             //this.iframe.className = '';
24678             this.deferFocus();
24679         }
24680         //this.setSize(this.owner.wrap.getSize());
24681         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24682     },
24683
24684     
24685   
24686
24687     /**
24688      * Protected method that will not generally be called directly. If you need/want
24689      * custom HTML cleanup, this is the method you should override.
24690      * @param {String} html The HTML to be cleaned
24691      * return {String} The cleaned HTML
24692      */
24693     cleanHtml : function(html){
24694         html = String(html);
24695         if(html.length > 5){
24696             if(Roo.isSafari){ // strip safari nonsense
24697                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24698             }
24699         }
24700         if(html == '&nbsp;'){
24701             html = '';
24702         }
24703         return html;
24704     },
24705
24706     /**
24707      * HTML Editor -> Textarea
24708      * Protected method that will not generally be called directly. Syncs the contents
24709      * of the editor iframe with the textarea.
24710      */
24711     syncValue : function(){
24712         if(this.initialized){
24713             var bd = (this.doc.body || this.doc.documentElement);
24714             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24715             var html = bd.innerHTML;
24716             if(Roo.isSafari){
24717                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24718                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24719                 if(m && m[1]){
24720                     html = '<div style="'+m[0]+'">' + html + '</div>';
24721                 }
24722             }
24723             html = this.cleanHtml(html);
24724             // fix up the special chars.. normaly like back quotes in word...
24725             // however we do not want to do this with chinese..
24726             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24727                 
24728                 var cc = match.charCodeAt();
24729
24730                 // Get the character value, handling surrogate pairs
24731                 if (match.length == 2) {
24732                     // It's a surrogate pair, calculate the Unicode code point
24733                     var high = match.charCodeAt(0) - 0xD800;
24734                     var low  = match.charCodeAt(1) - 0xDC00;
24735                     cc = (high * 0x400) + low + 0x10000;
24736                 }  else if (
24737                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24738                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24739                     (cc >= 0xf900 && cc < 0xfb00 )
24740                 ) {
24741                         return match;
24742                 }  
24743          
24744                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24745                 return "&#" + cc + ";";
24746                 
24747                 
24748             });
24749             
24750             
24751              
24752             if(this.owner.fireEvent('beforesync', this, html) !== false){
24753                 this.el.dom.value = html;
24754                 this.owner.fireEvent('sync', this, html);
24755             }
24756         }
24757     },
24758
24759     /**
24760      * Protected method that will not generally be called directly. Pushes the value of the textarea
24761      * into the iframe editor.
24762      */
24763     pushValue : function(){
24764         if(this.initialized){
24765             var v = this.el.dom.value.trim();
24766             
24767 //            if(v.length < 1){
24768 //                v = '&#160;';
24769 //            }
24770             
24771             if(this.owner.fireEvent('beforepush', this, v) !== false){
24772                 var d = (this.doc.body || this.doc.documentElement);
24773                 d.innerHTML = v;
24774                 this.cleanUpPaste();
24775                 this.el.dom.value = d.innerHTML;
24776                 this.owner.fireEvent('push', this, v);
24777             }
24778         }
24779     },
24780
24781     // private
24782     deferFocus : function(){
24783         this.focus.defer(10, this);
24784     },
24785
24786     // doc'ed in Field
24787     focus : function(){
24788         if(this.win && !this.sourceEditMode){
24789             this.win.focus();
24790         }else{
24791             this.el.focus();
24792         }
24793     },
24794     
24795     assignDocWin: function()
24796     {
24797         var iframe = this.iframe;
24798         
24799          if(Roo.isIE){
24800             this.doc = iframe.contentWindow.document;
24801             this.win = iframe.contentWindow;
24802         } else {
24803 //            if (!Roo.get(this.frameId)) {
24804 //                return;
24805 //            }
24806 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24807 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24808             
24809             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24810                 return;
24811             }
24812             
24813             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24815         }
24816     },
24817     
24818     // private
24819     initEditor : function(){
24820         //console.log("INIT EDITOR");
24821         this.assignDocWin();
24822         
24823         
24824         
24825         this.doc.designMode="on";
24826         this.doc.open();
24827         this.doc.write(this.getDocMarkup());
24828         this.doc.close();
24829         
24830         var dbody = (this.doc.body || this.doc.documentElement);
24831         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24832         // this copies styles from the containing element into thsi one..
24833         // not sure why we need all of this..
24834         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24835         
24836         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24837         //ss['background-attachment'] = 'fixed'; // w3c
24838         dbody.bgProperties = 'fixed'; // ie
24839         //Roo.DomHelper.applyStyles(dbody, ss);
24840         Roo.EventManager.on(this.doc, {
24841             //'mousedown': this.onEditorEvent,
24842             'mouseup': this.onEditorEvent,
24843             'dblclick': this.onEditorEvent,
24844             'click': this.onEditorEvent,
24845             'keyup': this.onEditorEvent,
24846             buffer:100,
24847             scope: this
24848         });
24849         if(Roo.isGecko){
24850             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24851         }
24852         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24853             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24854         }
24855         this.initialized = true;
24856
24857         this.owner.fireEvent('initialize', this);
24858         this.pushValue();
24859     },
24860
24861     // private
24862     onDestroy : function(){
24863         
24864         
24865         
24866         if(this.rendered){
24867             
24868             //for (var i =0; i < this.toolbars.length;i++) {
24869             //    // fixme - ask toolbars for heights?
24870             //    this.toolbars[i].onDestroy();
24871            // }
24872             
24873             //this.wrap.dom.innerHTML = '';
24874             //this.wrap.remove();
24875         }
24876     },
24877
24878     // private
24879     onFirstFocus : function(){
24880         
24881         this.assignDocWin();
24882         
24883         
24884         this.activated = true;
24885          
24886     
24887         if(Roo.isGecko){ // prevent silly gecko errors
24888             this.win.focus();
24889             var s = this.win.getSelection();
24890             if(!s.focusNode || s.focusNode.nodeType != 3){
24891                 var r = s.getRangeAt(0);
24892                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24893                 r.collapse(true);
24894                 this.deferFocus();
24895             }
24896             try{
24897                 this.execCmd('useCSS', true);
24898                 this.execCmd('styleWithCSS', false);
24899             }catch(e){}
24900         }
24901         this.owner.fireEvent('activate', this);
24902     },
24903
24904     // private
24905     adjustFont: function(btn){
24906         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24907         //if(Roo.isSafari){ // safari
24908         //    adjust *= 2;
24909        // }
24910         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24911         if(Roo.isSafari){ // safari
24912             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24913             v =  (v < 10) ? 10 : v;
24914             v =  (v > 48) ? 48 : v;
24915             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24916             
24917         }
24918         
24919         
24920         v = Math.max(1, v+adjust);
24921         
24922         this.execCmd('FontSize', v  );
24923     },
24924
24925     onEditorEvent : function(e)
24926     {
24927         this.owner.fireEvent('editorevent', this, e);
24928       //  this.updateToolbar();
24929         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24930     },
24931
24932     insertTag : function(tg)
24933     {
24934         // could be a bit smarter... -> wrap the current selected tRoo..
24935         if (tg.toLowerCase() == 'span' ||
24936             tg.toLowerCase() == 'code' ||
24937             tg.toLowerCase() == 'sup' ||
24938             tg.toLowerCase() == 'sub' 
24939             ) {
24940             
24941             range = this.createRange(this.getSelection());
24942             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24943             wrappingNode.appendChild(range.extractContents());
24944             range.insertNode(wrappingNode);
24945
24946             return;
24947             
24948             
24949             
24950         }
24951         this.execCmd("formatblock",   tg);
24952         
24953     },
24954     
24955     insertText : function(txt)
24956     {
24957         
24958         
24959         var range = this.createRange();
24960         range.deleteContents();
24961                //alert(Sender.getAttribute('label'));
24962                
24963         range.insertNode(this.doc.createTextNode(txt));
24964     } ,
24965     
24966      
24967
24968     /**
24969      * Executes a Midas editor command on the editor document and performs necessary focus and
24970      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24971      * @param {String} cmd The Midas command
24972      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24973      */
24974     relayCmd : function(cmd, value){
24975         this.win.focus();
24976         this.execCmd(cmd, value);
24977         this.owner.fireEvent('editorevent', this);
24978         //this.updateToolbar();
24979         this.owner.deferFocus();
24980     },
24981
24982     /**
24983      * Executes a Midas editor command directly on the editor document.
24984      * For visual commands, you should use {@link #relayCmd} instead.
24985      * <b>This should only be called after the editor is initialized.</b>
24986      * @param {String} cmd The Midas command
24987      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24988      */
24989     execCmd : function(cmd, value){
24990         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24991         this.syncValue();
24992     },
24993  
24994  
24995    
24996     /**
24997      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24998      * to insert tRoo.
24999      * @param {String} text | dom node.. 
25000      */
25001     insertAtCursor : function(text)
25002     {
25003         
25004         if(!this.activated){
25005             return;
25006         }
25007         /*
25008         if(Roo.isIE){
25009             this.win.focus();
25010             var r = this.doc.selection.createRange();
25011             if(r){
25012                 r.collapse(true);
25013                 r.pasteHTML(text);
25014                 this.syncValue();
25015                 this.deferFocus();
25016             
25017             }
25018             return;
25019         }
25020         */
25021         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25022             this.win.focus();
25023             
25024             
25025             // from jquery ui (MIT licenced)
25026             var range, node;
25027             var win = this.win;
25028             
25029             if (win.getSelection && win.getSelection().getRangeAt) {
25030                 range = win.getSelection().getRangeAt(0);
25031                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25032                 range.insertNode(node);
25033             } else if (win.document.selection && win.document.selection.createRange) {
25034                 // no firefox support
25035                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25036                 win.document.selection.createRange().pasteHTML(txt);
25037             } else {
25038                 // no firefox support
25039                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25040                 this.execCmd('InsertHTML', txt);
25041             } 
25042             
25043             this.syncValue();
25044             
25045             this.deferFocus();
25046         }
25047     },
25048  // private
25049     mozKeyPress : function(e){
25050         if(e.ctrlKey){
25051             var c = e.getCharCode(), cmd;
25052           
25053             if(c > 0){
25054                 c = String.fromCharCode(c).toLowerCase();
25055                 switch(c){
25056                     case 'b':
25057                         cmd = 'bold';
25058                         break;
25059                     case 'i':
25060                         cmd = 'italic';
25061                         break;
25062                     
25063                     case 'u':
25064                         cmd = 'underline';
25065                         break;
25066                     
25067                     case 'v':
25068                         this.cleanUpPaste.defer(100, this);
25069                         return;
25070                         
25071                 }
25072                 if(cmd){
25073                     this.win.focus();
25074                     this.execCmd(cmd);
25075                     this.deferFocus();
25076                     e.preventDefault();
25077                 }
25078                 
25079             }
25080         }
25081     },
25082
25083     // private
25084     fixKeys : function(){ // load time branching for fastest keydown performance
25085         if(Roo.isIE){
25086             return function(e){
25087                 var k = e.getKey(), r;
25088                 if(k == e.TAB){
25089                     e.stopEvent();
25090                     r = this.doc.selection.createRange();
25091                     if(r){
25092                         r.collapse(true);
25093                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25094                         this.deferFocus();
25095                     }
25096                     return;
25097                 }
25098                 
25099                 if(k == e.ENTER){
25100                     r = this.doc.selection.createRange();
25101                     if(r){
25102                         var target = r.parentElement();
25103                         if(!target || target.tagName.toLowerCase() != 'li'){
25104                             e.stopEvent();
25105                             r.pasteHTML('<br />');
25106                             r.collapse(false);
25107                             r.select();
25108                         }
25109                     }
25110                 }
25111                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25112                     this.cleanUpPaste.defer(100, this);
25113                     return;
25114                 }
25115                 
25116                 
25117             };
25118         }else if(Roo.isOpera){
25119             return function(e){
25120                 var k = e.getKey();
25121                 if(k == e.TAB){
25122                     e.stopEvent();
25123                     this.win.focus();
25124                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25125                     this.deferFocus();
25126                 }
25127                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128                     this.cleanUpPaste.defer(100, this);
25129                     return;
25130                 }
25131                 
25132             };
25133         }else if(Roo.isSafari){
25134             return function(e){
25135                 var k = e.getKey();
25136                 
25137                 if(k == e.TAB){
25138                     e.stopEvent();
25139                     this.execCmd('InsertText','\t');
25140                     this.deferFocus();
25141                     return;
25142                 }
25143                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144                     this.cleanUpPaste.defer(100, this);
25145                     return;
25146                 }
25147                 
25148              };
25149         }
25150     }(),
25151     
25152     getAllAncestors: function()
25153     {
25154         var p = this.getSelectedNode();
25155         var a = [];
25156         if (!p) {
25157             a.push(p); // push blank onto stack..
25158             p = this.getParentElement();
25159         }
25160         
25161         
25162         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25163             a.push(p);
25164             p = p.parentNode;
25165         }
25166         a.push(this.doc.body);
25167         return a;
25168     },
25169     lastSel : false,
25170     lastSelNode : false,
25171     
25172     
25173     getSelection : function() 
25174     {
25175         this.assignDocWin();
25176         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25177     },
25178     
25179     getSelectedNode: function() 
25180     {
25181         // this may only work on Gecko!!!
25182         
25183         // should we cache this!!!!
25184         
25185         
25186         
25187          
25188         var range = this.createRange(this.getSelection()).cloneRange();
25189         
25190         if (Roo.isIE) {
25191             var parent = range.parentElement();
25192             while (true) {
25193                 var testRange = range.duplicate();
25194                 testRange.moveToElementText(parent);
25195                 if (testRange.inRange(range)) {
25196                     break;
25197                 }
25198                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25199                     break;
25200                 }
25201                 parent = parent.parentElement;
25202             }
25203             return parent;
25204         }
25205         
25206         // is ancestor a text element.
25207         var ac =  range.commonAncestorContainer;
25208         if (ac.nodeType == 3) {
25209             ac = ac.parentNode;
25210         }
25211         
25212         var ar = ac.childNodes;
25213          
25214         var nodes = [];
25215         var other_nodes = [];
25216         var has_other_nodes = false;
25217         for (var i=0;i<ar.length;i++) {
25218             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25219                 continue;
25220             }
25221             // fullly contained node.
25222             
25223             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25224                 nodes.push(ar[i]);
25225                 continue;
25226             }
25227             
25228             // probably selected..
25229             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25230                 other_nodes.push(ar[i]);
25231                 continue;
25232             }
25233             // outer..
25234             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25235                 continue;
25236             }
25237             
25238             
25239             has_other_nodes = true;
25240         }
25241         if (!nodes.length && other_nodes.length) {
25242             nodes= other_nodes;
25243         }
25244         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25245             return false;
25246         }
25247         
25248         return nodes[0];
25249     },
25250     createRange: function(sel)
25251     {
25252         // this has strange effects when using with 
25253         // top toolbar - not sure if it's a great idea.
25254         //this.editor.contentWindow.focus();
25255         if (typeof sel != "undefined") {
25256             try {
25257                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25258             } catch(e) {
25259                 return this.doc.createRange();
25260             }
25261         } else {
25262             return this.doc.createRange();
25263         }
25264     },
25265     getParentElement: function()
25266     {
25267         
25268         this.assignDocWin();
25269         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25270         
25271         var range = this.createRange(sel);
25272          
25273         try {
25274             var p = range.commonAncestorContainer;
25275             while (p.nodeType == 3) { // text node
25276                 p = p.parentNode;
25277             }
25278             return p;
25279         } catch (e) {
25280             return null;
25281         }
25282     
25283     },
25284     /***
25285      *
25286      * Range intersection.. the hard stuff...
25287      *  '-1' = before
25288      *  '0' = hits..
25289      *  '1' = after.
25290      *         [ -- selected range --- ]
25291      *   [fail]                        [fail]
25292      *
25293      *    basically..
25294      *      if end is before start or  hits it. fail.
25295      *      if start is after end or hits it fail.
25296      *
25297      *   if either hits (but other is outside. - then it's not 
25298      *   
25299      *    
25300      **/
25301     
25302     
25303     // @see http://www.thismuchiknow.co.uk/?p=64.
25304     rangeIntersectsNode : function(range, node)
25305     {
25306         var nodeRange = node.ownerDocument.createRange();
25307         try {
25308             nodeRange.selectNode(node);
25309         } catch (e) {
25310             nodeRange.selectNodeContents(node);
25311         }
25312     
25313         var rangeStartRange = range.cloneRange();
25314         rangeStartRange.collapse(true);
25315     
25316         var rangeEndRange = range.cloneRange();
25317         rangeEndRange.collapse(false);
25318     
25319         var nodeStartRange = nodeRange.cloneRange();
25320         nodeStartRange.collapse(true);
25321     
25322         var nodeEndRange = nodeRange.cloneRange();
25323         nodeEndRange.collapse(false);
25324     
25325         return rangeStartRange.compareBoundaryPoints(
25326                  Range.START_TO_START, nodeEndRange) == -1 &&
25327                rangeEndRange.compareBoundaryPoints(
25328                  Range.START_TO_START, nodeStartRange) == 1;
25329         
25330          
25331     },
25332     rangeCompareNode : function(range, node)
25333     {
25334         var nodeRange = node.ownerDocument.createRange();
25335         try {
25336             nodeRange.selectNode(node);
25337         } catch (e) {
25338             nodeRange.selectNodeContents(node);
25339         }
25340         
25341         
25342         range.collapse(true);
25343     
25344         nodeRange.collapse(true);
25345      
25346         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25347         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25348          
25349         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25350         
25351         var nodeIsBefore   =  ss == 1;
25352         var nodeIsAfter    = ee == -1;
25353         
25354         if (nodeIsBefore && nodeIsAfter) {
25355             return 0; // outer
25356         }
25357         if (!nodeIsBefore && nodeIsAfter) {
25358             return 1; //right trailed.
25359         }
25360         
25361         if (nodeIsBefore && !nodeIsAfter) {
25362             return 2;  // left trailed.
25363         }
25364         // fully contined.
25365         return 3;
25366     },
25367
25368     // private? - in a new class?
25369     cleanUpPaste :  function()
25370     {
25371         // cleans up the whole document..
25372         Roo.log('cleanuppaste');
25373         
25374         this.cleanUpChildren(this.doc.body);
25375         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25376         if (clean != this.doc.body.innerHTML) {
25377             this.doc.body.innerHTML = clean;
25378         }
25379         
25380     },
25381     
25382     cleanWordChars : function(input) {// change the chars to hex code
25383         var he = Roo.HtmlEditorCore;
25384         
25385         var output = input;
25386         Roo.each(he.swapCodes, function(sw) { 
25387             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25388             
25389             output = output.replace(swapper, sw[1]);
25390         });
25391         
25392         return output;
25393     },
25394     
25395     
25396     cleanUpChildren : function (n)
25397     {
25398         if (!n.childNodes.length) {
25399             return;
25400         }
25401         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25402            this.cleanUpChild(n.childNodes[i]);
25403         }
25404     },
25405     
25406     
25407         
25408     
25409     cleanUpChild : function (node)
25410     {
25411         var ed = this;
25412         //console.log(node);
25413         if (node.nodeName == "#text") {
25414             // clean up silly Windows -- stuff?
25415             return; 
25416         }
25417         if (node.nodeName == "#comment") {
25418             node.parentNode.removeChild(node);
25419             // clean up silly Windows -- stuff?
25420             return; 
25421         }
25422         var lcname = node.tagName.toLowerCase();
25423         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25424         // whitelist of tags..
25425         
25426         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25427             // remove node.
25428             node.parentNode.removeChild(node);
25429             return;
25430             
25431         }
25432         
25433         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25434         
25435         // spans with no attributes - just remove them..
25436         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25437             remove_keep_children = true;
25438         }
25439         
25440         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25441         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25442         
25443         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25444         //    remove_keep_children = true;
25445         //}
25446         
25447         if (remove_keep_children) {
25448             this.cleanUpChildren(node);
25449             // inserts everything just before this node...
25450             while (node.childNodes.length) {
25451                 var cn = node.childNodes[0];
25452                 node.removeChild(cn);
25453                 node.parentNode.insertBefore(cn, node);
25454             }
25455             node.parentNode.removeChild(node);
25456             return;
25457         }
25458         
25459         if (!node.attributes || !node.attributes.length) {
25460             
25461           
25462             
25463             
25464             this.cleanUpChildren(node);
25465             return;
25466         }
25467         
25468         function cleanAttr(n,v)
25469         {
25470             
25471             if (v.match(/^\./) || v.match(/^\//)) {
25472                 return;
25473             }
25474             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25475                 return;
25476             }
25477             if (v.match(/^#/)) {
25478                 return;
25479             }
25480             if (v.match(/^\{/)) { // allow template editing.
25481                 return;
25482             }
25483 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25484             node.removeAttribute(n);
25485             
25486         }
25487         
25488         var cwhite = this.cwhite;
25489         var cblack = this.cblack;
25490             
25491         function cleanStyle(n,v)
25492         {
25493             if (v.match(/expression/)) { //XSS?? should we even bother..
25494                 node.removeAttribute(n);
25495                 return;
25496             }
25497             
25498             var parts = v.split(/;/);
25499             var clean = [];
25500             
25501             Roo.each(parts, function(p) {
25502                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25503                 if (!p.length) {
25504                     return true;
25505                 }
25506                 var l = p.split(':').shift().replace(/\s+/g,'');
25507                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25508                 
25509                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25510 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25511                     //node.removeAttribute(n);
25512                     return true;
25513                 }
25514                 //Roo.log()
25515                 // only allow 'c whitelisted system attributes'
25516                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25517 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518                     //node.removeAttribute(n);
25519                     return true;
25520                 }
25521                 
25522                 
25523                  
25524                 
25525                 clean.push(p);
25526                 return true;
25527             });
25528             if (clean.length) { 
25529                 node.setAttribute(n, clean.join(';'));
25530             } else {
25531                 node.removeAttribute(n);
25532             }
25533             
25534         }
25535         
25536         
25537         for (var i = node.attributes.length-1; i > -1 ; i--) {
25538             var a = node.attributes[i];
25539             //console.log(a);
25540             
25541             if (a.name.toLowerCase().substr(0,2)=='on')  {
25542                 node.removeAttribute(a.name);
25543                 continue;
25544             }
25545             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25546                 node.removeAttribute(a.name);
25547                 continue;
25548             }
25549             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25550                 cleanAttr(a.name,a.value); // fixme..
25551                 continue;
25552             }
25553             if (a.name == 'style') {
25554                 cleanStyle(a.name,a.value);
25555                 continue;
25556             }
25557             /// clean up MS crap..
25558             // tecnically this should be a list of valid class'es..
25559             
25560             
25561             if (a.name == 'class') {
25562                 if (a.value.match(/^Mso/)) {
25563                     node.removeAttribute('class');
25564                 }
25565                 
25566                 if (a.value.match(/^body$/)) {
25567                     node.removeAttribute('class');
25568                 }
25569                 continue;
25570             }
25571             
25572             // style cleanup!?
25573             // class cleanup?
25574             
25575         }
25576         
25577         
25578         this.cleanUpChildren(node);
25579         
25580         
25581     },
25582     
25583     /**
25584      * Clean up MS wordisms...
25585      */
25586     cleanWord : function(node)
25587     {
25588         if (!node) {
25589             this.cleanWord(this.doc.body);
25590             return;
25591         }
25592         
25593         if(
25594                 node.nodeName == 'SPAN' &&
25595                 !node.hasAttributes() &&
25596                 node.childNodes.length == 1 &&
25597                 node.firstChild.nodeName == "#text"  
25598         ) {
25599             var textNode = node.firstChild;
25600             node.removeChild(textNode);
25601             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25602                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25603             }
25604             node.parentNode.insertBefore(textNode, node);
25605             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25606                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25607             }
25608             node.parentNode.removeChild(node);
25609         }
25610         
25611         if (node.nodeName == "#text") {
25612             // clean up silly Windows -- stuff?
25613             return; 
25614         }
25615         if (node.nodeName == "#comment") {
25616             node.parentNode.removeChild(node);
25617             // clean up silly Windows -- stuff?
25618             return; 
25619         }
25620         
25621         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25622             node.parentNode.removeChild(node);
25623             return;
25624         }
25625         //Roo.log(node.tagName);
25626         // remove - but keep children..
25627         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25628             //Roo.log('-- removed');
25629             while (node.childNodes.length) {
25630                 var cn = node.childNodes[0];
25631                 node.removeChild(cn);
25632                 node.parentNode.insertBefore(cn, node);
25633                 // move node to parent - and clean it..
25634                 this.cleanWord(cn);
25635             }
25636             node.parentNode.removeChild(node);
25637             /// no need to iterate chidlren = it's got none..
25638             //this.iterateChildren(node, this.cleanWord);
25639             return;
25640         }
25641         // clean styles
25642         if (node.className.length) {
25643             
25644             var cn = node.className.split(/\W+/);
25645             var cna = [];
25646             Roo.each(cn, function(cls) {
25647                 if (cls.match(/Mso[a-zA-Z]+/)) {
25648                     return;
25649                 }
25650                 cna.push(cls);
25651             });
25652             node.className = cna.length ? cna.join(' ') : '';
25653             if (!cna.length) {
25654                 node.removeAttribute("class");
25655             }
25656         }
25657         
25658         if (node.hasAttribute("lang")) {
25659             node.removeAttribute("lang");
25660         }
25661         
25662         if (node.hasAttribute("style")) {
25663             
25664             var styles = node.getAttribute("style").split(";");
25665             var nstyle = [];
25666             Roo.each(styles, function(s) {
25667                 if (!s.match(/:/)) {
25668                     return;
25669                 }
25670                 var kv = s.split(":");
25671                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25672                     return;
25673                 }
25674                 // what ever is left... we allow.
25675                 nstyle.push(s);
25676             });
25677             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25678             if (!nstyle.length) {
25679                 node.removeAttribute('style');
25680             }
25681         }
25682         this.iterateChildren(node, this.cleanWord);
25683         
25684         
25685         
25686     },
25687     /**
25688      * iterateChildren of a Node, calling fn each time, using this as the scole..
25689      * @param {DomNode} node node to iterate children of.
25690      * @param {Function} fn method of this class to call on each item.
25691      */
25692     iterateChildren : function(node, fn)
25693     {
25694         if (!node.childNodes.length) {
25695                 return;
25696         }
25697         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25698            fn.call(this, node.childNodes[i])
25699         }
25700     },
25701     
25702     
25703     /**
25704      * cleanTableWidths.
25705      *
25706      * Quite often pasting from word etc.. results in tables with column and widths.
25707      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25708      *
25709      */
25710     cleanTableWidths : function(node)
25711     {
25712          
25713          
25714         if (!node) {
25715             this.cleanTableWidths(this.doc.body);
25716             return;
25717         }
25718         
25719         // ignore list...
25720         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25721             return; 
25722         }
25723         Roo.log(node.tagName);
25724         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25725             this.iterateChildren(node, this.cleanTableWidths);
25726             return;
25727         }
25728         if (node.hasAttribute('width')) {
25729             node.removeAttribute('width');
25730         }
25731         
25732          
25733         if (node.hasAttribute("style")) {
25734             // pretty basic...
25735             
25736             var styles = node.getAttribute("style").split(";");
25737             var nstyle = [];
25738             Roo.each(styles, function(s) {
25739                 if (!s.match(/:/)) {
25740                     return;
25741                 }
25742                 var kv = s.split(":");
25743                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25744                     return;
25745                 }
25746                 // what ever is left... we allow.
25747                 nstyle.push(s);
25748             });
25749             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25750             if (!nstyle.length) {
25751                 node.removeAttribute('style');
25752             }
25753         }
25754         
25755         this.iterateChildren(node, this.cleanTableWidths);
25756         
25757         
25758     },
25759     
25760     
25761     
25762     
25763     domToHTML : function(currentElement, depth, nopadtext) {
25764         
25765         depth = depth || 0;
25766         nopadtext = nopadtext || false;
25767     
25768         if (!currentElement) {
25769             return this.domToHTML(this.doc.body);
25770         }
25771         
25772         //Roo.log(currentElement);
25773         var j;
25774         var allText = false;
25775         var nodeName = currentElement.nodeName;
25776         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25777         
25778         if  (nodeName == '#text') {
25779             
25780             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25781         }
25782         
25783         
25784         var ret = '';
25785         if (nodeName != 'BODY') {
25786              
25787             var i = 0;
25788             // Prints the node tagName, such as <A>, <IMG>, etc
25789             if (tagName) {
25790                 var attr = [];
25791                 for(i = 0; i < currentElement.attributes.length;i++) {
25792                     // quoting?
25793                     var aname = currentElement.attributes.item(i).name;
25794                     if (!currentElement.attributes.item(i).value.length) {
25795                         continue;
25796                     }
25797                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25798                 }
25799                 
25800                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25801             } 
25802             else {
25803                 
25804                 // eack
25805             }
25806         } else {
25807             tagName = false;
25808         }
25809         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25810             return ret;
25811         }
25812         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25813             nopadtext = true;
25814         }
25815         
25816         
25817         // Traverse the tree
25818         i = 0;
25819         var currentElementChild = currentElement.childNodes.item(i);
25820         var allText = true;
25821         var innerHTML  = '';
25822         lastnode = '';
25823         while (currentElementChild) {
25824             // Formatting code (indent the tree so it looks nice on the screen)
25825             var nopad = nopadtext;
25826             if (lastnode == 'SPAN') {
25827                 nopad  = true;
25828             }
25829             // text
25830             if  (currentElementChild.nodeName == '#text') {
25831                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25832                 toadd = nopadtext ? toadd : toadd.trim();
25833                 if (!nopad && toadd.length > 80) {
25834                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25835                 }
25836                 innerHTML  += toadd;
25837                 
25838                 i++;
25839                 currentElementChild = currentElement.childNodes.item(i);
25840                 lastNode = '';
25841                 continue;
25842             }
25843             allText = false;
25844             
25845             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25846                 
25847             // Recursively traverse the tree structure of the child node
25848             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25849             lastnode = currentElementChild.nodeName;
25850             i++;
25851             currentElementChild=currentElement.childNodes.item(i);
25852         }
25853         
25854         ret += innerHTML;
25855         
25856         if (!allText) {
25857                 // The remaining code is mostly for formatting the tree
25858             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25859         }
25860         
25861         
25862         if (tagName) {
25863             ret+= "</"+tagName+">";
25864         }
25865         return ret;
25866         
25867     },
25868         
25869     applyBlacklists : function()
25870     {
25871         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25872         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25873         
25874         this.white = [];
25875         this.black = [];
25876         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25877             if (b.indexOf(tag) > -1) {
25878                 return;
25879             }
25880             this.white.push(tag);
25881             
25882         }, this);
25883         
25884         Roo.each(w, function(tag) {
25885             if (b.indexOf(tag) > -1) {
25886                 return;
25887             }
25888             if (this.white.indexOf(tag) > -1) {
25889                 return;
25890             }
25891             this.white.push(tag);
25892             
25893         }, this);
25894         
25895         
25896         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25897             if (w.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.black.push(tag);
25901             
25902         }, this);
25903         
25904         Roo.each(b, function(tag) {
25905             if (w.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             if (this.black.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             this.black.push(tag);
25912             
25913         }, this);
25914         
25915         
25916         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25917         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25918         
25919         this.cwhite = [];
25920         this.cblack = [];
25921         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25922             if (b.indexOf(tag) > -1) {
25923                 return;
25924             }
25925             this.cwhite.push(tag);
25926             
25927         }, this);
25928         
25929         Roo.each(w, function(tag) {
25930             if (b.indexOf(tag) > -1) {
25931                 return;
25932             }
25933             if (this.cwhite.indexOf(tag) > -1) {
25934                 return;
25935             }
25936             this.cwhite.push(tag);
25937             
25938         }, this);
25939         
25940         
25941         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25942             if (w.indexOf(tag) > -1) {
25943                 return;
25944             }
25945             this.cblack.push(tag);
25946             
25947         }, this);
25948         
25949         Roo.each(b, function(tag) {
25950             if (w.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             if (this.cblack.indexOf(tag) > -1) {
25954                 return;
25955             }
25956             this.cblack.push(tag);
25957             
25958         }, this);
25959     },
25960     
25961     setStylesheets : function(stylesheets)
25962     {
25963         if(typeof(stylesheets) == 'string'){
25964             Roo.get(this.iframe.contentDocument.head).createChild({
25965                 tag : 'link',
25966                 rel : 'stylesheet',
25967                 type : 'text/css',
25968                 href : stylesheets
25969             });
25970             
25971             return;
25972         }
25973         var _this = this;
25974      
25975         Roo.each(stylesheets, function(s) {
25976             if(!s.length){
25977                 return;
25978             }
25979             
25980             Roo.get(_this.iframe.contentDocument.head).createChild({
25981                 tag : 'link',
25982                 rel : 'stylesheet',
25983                 type : 'text/css',
25984                 href : s
25985             });
25986         });
25987
25988         
25989     },
25990     
25991     removeStylesheets : function()
25992     {
25993         var _this = this;
25994         
25995         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25996             s.remove();
25997         });
25998     },
25999     
26000     setStyle : function(style)
26001     {
26002         Roo.get(this.iframe.contentDocument.head).createChild({
26003             tag : 'style',
26004             type : 'text/css',
26005             html : style
26006         });
26007
26008         return;
26009     }
26010     
26011     // hide stuff that is not compatible
26012     /**
26013      * @event blur
26014      * @hide
26015      */
26016     /**
26017      * @event change
26018      * @hide
26019      */
26020     /**
26021      * @event focus
26022      * @hide
26023      */
26024     /**
26025      * @event specialkey
26026      * @hide
26027      */
26028     /**
26029      * @cfg {String} fieldClass @hide
26030      */
26031     /**
26032      * @cfg {String} focusClass @hide
26033      */
26034     /**
26035      * @cfg {String} autoCreate @hide
26036      */
26037     /**
26038      * @cfg {String} inputType @hide
26039      */
26040     /**
26041      * @cfg {String} invalidClass @hide
26042      */
26043     /**
26044      * @cfg {String} invalidText @hide
26045      */
26046     /**
26047      * @cfg {String} msgFx @hide
26048      */
26049     /**
26050      * @cfg {String} validateOnBlur @hide
26051      */
26052 });
26053
26054 Roo.HtmlEditorCore.white = [
26055         'area', 'br', 'img', 'input', 'hr', 'wbr',
26056         
26057        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26058        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26059        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26060        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26061        'table',   'ul',         'xmp', 
26062        
26063        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26064       'thead',   'tr', 
26065      
26066       'dir', 'menu', 'ol', 'ul', 'dl',
26067        
26068       'embed',  'object'
26069 ];
26070
26071
26072 Roo.HtmlEditorCore.black = [
26073     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26074         'applet', // 
26075         'base',   'basefont', 'bgsound', 'blink',  'body', 
26076         'frame',  'frameset', 'head',    'html',   'ilayer', 
26077         'iframe', 'layer',  'link',     'meta',    'object',   
26078         'script', 'style' ,'title',  'xml' // clean later..
26079 ];
26080 Roo.HtmlEditorCore.clean = [
26081     'script', 'style', 'title', 'xml'
26082 ];
26083 Roo.HtmlEditorCore.remove = [
26084     'font'
26085 ];
26086 // attributes..
26087
26088 Roo.HtmlEditorCore.ablack = [
26089     'on'
26090 ];
26091     
26092 Roo.HtmlEditorCore.aclean = [ 
26093     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26094 ];
26095
26096 // protocols..
26097 Roo.HtmlEditorCore.pwhite= [
26098         'http',  'https',  'mailto'
26099 ];
26100
26101 // white listed style attributes.
26102 Roo.HtmlEditorCore.cwhite= [
26103       //  'text-align', /// default is to allow most things..
26104       
26105          
26106 //        'font-size'//??
26107 ];
26108
26109 // black listed style attributes.
26110 Roo.HtmlEditorCore.cblack= [
26111       //  'font-size' -- this can be set by the project 
26112 ];
26113
26114
26115 Roo.HtmlEditorCore.swapCodes   =[ 
26116     [    8211, "&#8211;" ], 
26117     [    8212, "&#8212;" ], 
26118     [    8216,  "'" ],  
26119     [    8217, "'" ],  
26120     [    8220, '"' ],  
26121     [    8221, '"' ],  
26122     [    8226, "*" ],  
26123     [    8230, "..." ]
26124 ]; 
26125
26126     /*
26127  * - LGPL
26128  *
26129  * HtmlEditor
26130  * 
26131  */
26132
26133 /**
26134  * @class Roo.bootstrap.HtmlEditor
26135  * @extends Roo.bootstrap.TextArea
26136  * Bootstrap HtmlEditor class
26137
26138  * @constructor
26139  * Create a new HtmlEditor
26140  * @param {Object} config The config object
26141  */
26142
26143 Roo.bootstrap.HtmlEditor = function(config){
26144     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26145     if (!this.toolbars) {
26146         this.toolbars = [];
26147     }
26148     
26149     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26150     this.addEvents({
26151             /**
26152              * @event initialize
26153              * Fires when the editor is fully initialized (including the iframe)
26154              * @param {HtmlEditor} this
26155              */
26156             initialize: true,
26157             /**
26158              * @event activate
26159              * Fires when the editor is first receives the focus. Any insertion must wait
26160              * until after this event.
26161              * @param {HtmlEditor} this
26162              */
26163             activate: true,
26164              /**
26165              * @event beforesync
26166              * Fires before the textarea is updated with content from the editor iframe. Return false
26167              * to cancel the sync.
26168              * @param {HtmlEditor} this
26169              * @param {String} html
26170              */
26171             beforesync: true,
26172              /**
26173              * @event beforepush
26174              * Fires before the iframe editor is updated with content from the textarea. Return false
26175              * to cancel the push.
26176              * @param {HtmlEditor} this
26177              * @param {String} html
26178              */
26179             beforepush: true,
26180              /**
26181              * @event sync
26182              * Fires when the textarea is updated with content from the editor iframe.
26183              * @param {HtmlEditor} this
26184              * @param {String} html
26185              */
26186             sync: true,
26187              /**
26188              * @event push
26189              * Fires when the iframe editor is updated with content from the textarea.
26190              * @param {HtmlEditor} this
26191              * @param {String} html
26192              */
26193             push: true,
26194              /**
26195              * @event editmodechange
26196              * Fires when the editor switches edit modes
26197              * @param {HtmlEditor} this
26198              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26199              */
26200             editmodechange: true,
26201             /**
26202              * @event editorevent
26203              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26204              * @param {HtmlEditor} this
26205              */
26206             editorevent: true,
26207             /**
26208              * @event firstfocus
26209              * Fires when on first focus - needed by toolbars..
26210              * @param {HtmlEditor} this
26211              */
26212             firstfocus: true,
26213             /**
26214              * @event autosave
26215              * Auto save the htmlEditor value as a file into Events
26216              * @param {HtmlEditor} this
26217              */
26218             autosave: true,
26219             /**
26220              * @event savedpreview
26221              * preview the saved version of htmlEditor
26222              * @param {HtmlEditor} this
26223              */
26224             savedpreview: true
26225         });
26226 };
26227
26228
26229 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26230     
26231     
26232       /**
26233      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26234      */
26235     toolbars : false,
26236     
26237      /**
26238     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26239     */
26240     btns : [],
26241    
26242      /**
26243      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26244      *                        Roo.resizable.
26245      */
26246     resizable : false,
26247      /**
26248      * @cfg {Number} height (in pixels)
26249      */   
26250     height: 300,
26251    /**
26252      * @cfg {Number} width (in pixels)
26253      */   
26254     width: false,
26255     
26256     /**
26257      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26258      * 
26259      */
26260     stylesheets: false,
26261     
26262     // id of frame..
26263     frameId: false,
26264     
26265     // private properties
26266     validationEvent : false,
26267     deferHeight: true,
26268     initialized : false,
26269     activated : false,
26270     
26271     onFocus : Roo.emptyFn,
26272     iframePad:3,
26273     hideMode:'offsets',
26274     
26275     tbContainer : false,
26276     
26277     bodyCls : '',
26278     
26279     toolbarContainer :function() {
26280         return this.wrap.select('.x-html-editor-tb',true).first();
26281     },
26282
26283     /**
26284      * Protected method that will not generally be called directly. It
26285      * is called when the editor creates its toolbar. Override this method if you need to
26286      * add custom toolbar buttons.
26287      * @param {HtmlEditor} editor
26288      */
26289     createToolbar : function(){
26290         Roo.log('renewing');
26291         Roo.log("create toolbars");
26292         
26293         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26294         this.toolbars[0].render(this.toolbarContainer());
26295         
26296         return;
26297         
26298 //        if (!editor.toolbars || !editor.toolbars.length) {
26299 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26300 //        }
26301 //        
26302 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26303 //            editor.toolbars[i] = Roo.factory(
26304 //                    typeof(editor.toolbars[i]) == 'string' ?
26305 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26306 //                Roo.bootstrap.HtmlEditor);
26307 //            editor.toolbars[i].init(editor);
26308 //        }
26309     },
26310
26311      
26312     // private
26313     onRender : function(ct, position)
26314     {
26315        // Roo.log("Call onRender: " + this.xtype);
26316         var _t = this;
26317         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26318       
26319         this.wrap = this.inputEl().wrap({
26320             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26321         });
26322         
26323         this.editorcore.onRender(ct, position);
26324          
26325         if (this.resizable) {
26326             this.resizeEl = new Roo.Resizable(this.wrap, {
26327                 pinned : true,
26328                 wrap: true,
26329                 dynamic : true,
26330                 minHeight : this.height,
26331                 height: this.height,
26332                 handles : this.resizable,
26333                 width: this.width,
26334                 listeners : {
26335                     resize : function(r, w, h) {
26336                         _t.onResize(w,h); // -something
26337                     }
26338                 }
26339             });
26340             
26341         }
26342         this.createToolbar(this);
26343        
26344         
26345         if(!this.width && this.resizable){
26346             this.setSize(this.wrap.getSize());
26347         }
26348         if (this.resizeEl) {
26349             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26350             // should trigger onReize..
26351         }
26352         
26353     },
26354
26355     // private
26356     onResize : function(w, h)
26357     {
26358         Roo.log('resize: ' +w + ',' + h );
26359         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26360         var ew = false;
26361         var eh = false;
26362         
26363         if(this.inputEl() ){
26364             if(typeof w == 'number'){
26365                 var aw = w - this.wrap.getFrameWidth('lr');
26366                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26367                 ew = aw;
26368             }
26369             if(typeof h == 'number'){
26370                  var tbh = -11;  // fixme it needs to tool bar size!
26371                 for (var i =0; i < this.toolbars.length;i++) {
26372                     // fixme - ask toolbars for heights?
26373                     tbh += this.toolbars[i].el.getHeight();
26374                     //if (this.toolbars[i].footer) {
26375                     //    tbh += this.toolbars[i].footer.el.getHeight();
26376                     //}
26377                 }
26378               
26379                 
26380                 
26381                 
26382                 
26383                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26384                 ah -= 5; // knock a few pixes off for look..
26385                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26386                 var eh = ah;
26387             }
26388         }
26389         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26390         this.editorcore.onResize(ew,eh);
26391         
26392     },
26393
26394     /**
26395      * Toggles the editor between standard and source edit mode.
26396      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26397      */
26398     toggleSourceEdit : function(sourceEditMode)
26399     {
26400         this.editorcore.toggleSourceEdit(sourceEditMode);
26401         
26402         if(this.editorcore.sourceEditMode){
26403             Roo.log('editor - showing textarea');
26404             
26405 //            Roo.log('in');
26406 //            Roo.log(this.syncValue());
26407             this.syncValue();
26408             this.inputEl().removeClass(['hide', 'x-hidden']);
26409             this.inputEl().dom.removeAttribute('tabIndex');
26410             this.inputEl().focus();
26411         }else{
26412             Roo.log('editor - hiding textarea');
26413 //            Roo.log('out')
26414 //            Roo.log(this.pushValue()); 
26415             this.pushValue();
26416             
26417             this.inputEl().addClass(['hide', 'x-hidden']);
26418             this.inputEl().dom.setAttribute('tabIndex', -1);
26419             //this.deferFocus();
26420         }
26421          
26422         if(this.resizable){
26423             this.setSize(this.wrap.getSize());
26424         }
26425         
26426         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26427     },
26428  
26429     // private (for BoxComponent)
26430     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26431
26432     // private (for BoxComponent)
26433     getResizeEl : function(){
26434         return this.wrap;
26435     },
26436
26437     // private (for BoxComponent)
26438     getPositionEl : function(){
26439         return this.wrap;
26440     },
26441
26442     // private
26443     initEvents : function(){
26444         this.originalValue = this.getValue();
26445     },
26446
26447 //    /**
26448 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26449 //     * @method
26450 //     */
26451 //    markInvalid : Roo.emptyFn,
26452 //    /**
26453 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26454 //     * @method
26455 //     */
26456 //    clearInvalid : Roo.emptyFn,
26457
26458     setValue : function(v){
26459         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26460         this.editorcore.pushValue();
26461     },
26462
26463      
26464     // private
26465     deferFocus : function(){
26466         this.focus.defer(10, this);
26467     },
26468
26469     // doc'ed in Field
26470     focus : function(){
26471         this.editorcore.focus();
26472         
26473     },
26474       
26475
26476     // private
26477     onDestroy : function(){
26478         
26479         
26480         
26481         if(this.rendered){
26482             
26483             for (var i =0; i < this.toolbars.length;i++) {
26484                 // fixme - ask toolbars for heights?
26485                 this.toolbars[i].onDestroy();
26486             }
26487             
26488             this.wrap.dom.innerHTML = '';
26489             this.wrap.remove();
26490         }
26491     },
26492
26493     // private
26494     onFirstFocus : function(){
26495         //Roo.log("onFirstFocus");
26496         this.editorcore.onFirstFocus();
26497          for (var i =0; i < this.toolbars.length;i++) {
26498             this.toolbars[i].onFirstFocus();
26499         }
26500         
26501     },
26502     
26503     // private
26504     syncValue : function()
26505     {   
26506         this.editorcore.syncValue();
26507     },
26508     
26509     pushValue : function()
26510     {   
26511         this.editorcore.pushValue();
26512     }
26513      
26514     
26515     // hide stuff that is not compatible
26516     /**
26517      * @event blur
26518      * @hide
26519      */
26520     /**
26521      * @event change
26522      * @hide
26523      */
26524     /**
26525      * @event focus
26526      * @hide
26527      */
26528     /**
26529      * @event specialkey
26530      * @hide
26531      */
26532     /**
26533      * @cfg {String} fieldClass @hide
26534      */
26535     /**
26536      * @cfg {String} focusClass @hide
26537      */
26538     /**
26539      * @cfg {String} autoCreate @hide
26540      */
26541     /**
26542      * @cfg {String} inputType @hide
26543      */
26544      
26545     /**
26546      * @cfg {String} invalidText @hide
26547      */
26548     /**
26549      * @cfg {String} msgFx @hide
26550      */
26551     /**
26552      * @cfg {String} validateOnBlur @hide
26553      */
26554 });
26555  
26556     
26557    
26558    
26559    
26560       
26561 Roo.namespace('Roo.bootstrap.htmleditor');
26562 /**
26563  * @class Roo.bootstrap.HtmlEditorToolbar1
26564  * Basic Toolbar
26565  * 
26566  * @example
26567  * Usage:
26568  *
26569  new Roo.bootstrap.HtmlEditor({
26570     ....
26571     toolbars : [
26572         new Roo.bootstrap.HtmlEditorToolbar1({
26573             disable : { fonts: 1 , format: 1, ..., ... , ...],
26574             btns : [ .... ]
26575         })
26576     }
26577      
26578  * 
26579  * @cfg {Object} disable List of elements to disable..
26580  * @cfg {Array} btns List of additional buttons.
26581  * 
26582  * 
26583  * NEEDS Extra CSS? 
26584  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26585  */
26586  
26587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26588 {
26589     
26590     Roo.apply(this, config);
26591     
26592     // default disabled, based on 'good practice'..
26593     this.disable = this.disable || {};
26594     Roo.applyIf(this.disable, {
26595         fontSize : true,
26596         colors : true,
26597         specialElements : true
26598     });
26599     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26600     
26601     this.editor = config.editor;
26602     this.editorcore = config.editor.editorcore;
26603     
26604     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26605     
26606     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26607     // dont call parent... till later.
26608 }
26609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26610      
26611     bar : true,
26612     
26613     editor : false,
26614     editorcore : false,
26615     
26616     
26617     formats : [
26618         "p" ,  
26619         "h1","h2","h3","h4","h5","h6", 
26620         "pre", "code", 
26621         "abbr", "acronym", "address", "cite", "samp", "var",
26622         'div','span'
26623     ],
26624     
26625     onRender : function(ct, position)
26626     {
26627        // Roo.log("Call onRender: " + this.xtype);
26628         
26629        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26630        Roo.log(this.el);
26631        this.el.dom.style.marginBottom = '0';
26632        var _this = this;
26633        var editorcore = this.editorcore;
26634        var editor= this.editor;
26635        
26636        var children = [];
26637        var btn = function(id,cmd , toggle, handler, html){
26638        
26639             var  event = toggle ? 'toggle' : 'click';
26640        
26641             var a = {
26642                 size : 'sm',
26643                 xtype: 'Button',
26644                 xns: Roo.bootstrap,
26645                 //glyphicon : id,
26646                 fa: id,
26647                 cmd : id || cmd,
26648                 enableToggle:toggle !== false,
26649                 html : html || '',
26650                 pressed : toggle ? false : null,
26651                 listeners : {}
26652             };
26653             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26654                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26655             };
26656             children.push(a);
26657             return a;
26658        }
26659        
26660     //    var cb_box = function...
26661         
26662         var style = {
26663                 xtype: 'Button',
26664                 size : 'sm',
26665                 xns: Roo.bootstrap,
26666                 fa : 'font',
26667                 //html : 'submit'
26668                 menu : {
26669                     xtype: 'Menu',
26670                     xns: Roo.bootstrap,
26671                     items:  []
26672                 }
26673         };
26674         Roo.each(this.formats, function(f) {
26675             style.menu.items.push({
26676                 xtype :'MenuItem',
26677                 xns: Roo.bootstrap,
26678                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26679                 tagname : f,
26680                 listeners : {
26681                     click : function()
26682                     {
26683                         editorcore.insertTag(this.tagname);
26684                         editor.focus();
26685                     }
26686                 }
26687                 
26688             });
26689         });
26690         children.push(style);   
26691         
26692         btn('bold',false,true);
26693         btn('italic',false,true);
26694         btn('align-left', 'justifyleft',true);
26695         btn('align-center', 'justifycenter',true);
26696         btn('align-right' , 'justifyright',true);
26697         btn('link', false, false, function(btn) {
26698             //Roo.log("create link?");
26699             var url = prompt(this.createLinkText, this.defaultLinkValue);
26700             if(url && url != 'http:/'+'/'){
26701                 this.editorcore.relayCmd('createlink', url);
26702             }
26703         }),
26704         btn('list','insertunorderedlist',true);
26705         btn('pencil', false,true, function(btn){
26706                 Roo.log(this);
26707                 this.toggleSourceEdit(btn.pressed);
26708         });
26709         
26710         if (this.editor.btns.length > 0) {
26711             for (var i = 0; i<this.editor.btns.length; i++) {
26712                 children.push(this.editor.btns[i]);
26713             }
26714         }
26715         
26716         /*
26717         var cog = {
26718                 xtype: 'Button',
26719                 size : 'sm',
26720                 xns: Roo.bootstrap,
26721                 glyphicon : 'cog',
26722                 //html : 'submit'
26723                 menu : {
26724                     xtype: 'Menu',
26725                     xns: Roo.bootstrap,
26726                     items:  []
26727                 }
26728         };
26729         
26730         cog.menu.items.push({
26731             xtype :'MenuItem',
26732             xns: Roo.bootstrap,
26733             html : Clean styles,
26734             tagname : f,
26735             listeners : {
26736                 click : function()
26737                 {
26738                     editorcore.insertTag(this.tagname);
26739                     editor.focus();
26740                 }
26741             }
26742             
26743         });
26744        */
26745         
26746          
26747        this.xtype = 'NavSimplebar';
26748         
26749         for(var i=0;i< children.length;i++) {
26750             
26751             this.buttons.add(this.addxtypeChild(children[i]));
26752             
26753         }
26754         
26755         editor.on('editorevent', this.updateToolbar, this);
26756     },
26757     onBtnClick : function(id)
26758     {
26759        this.editorcore.relayCmd(id);
26760        this.editorcore.focus();
26761     },
26762     
26763     /**
26764      * Protected method that will not generally be called directly. It triggers
26765      * a toolbar update by reading the markup state of the current selection in the editor.
26766      */
26767     updateToolbar: function(){
26768
26769         if(!this.editorcore.activated){
26770             this.editor.onFirstFocus(); // is this neeed?
26771             return;
26772         }
26773
26774         var btns = this.buttons; 
26775         var doc = this.editorcore.doc;
26776         btns.get('bold').setActive(doc.queryCommandState('bold'));
26777         btns.get('italic').setActive(doc.queryCommandState('italic'));
26778         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26779         
26780         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26781         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26782         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26783         
26784         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26785         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26786          /*
26787         
26788         var ans = this.editorcore.getAllAncestors();
26789         if (this.formatCombo) {
26790             
26791             
26792             var store = this.formatCombo.store;
26793             this.formatCombo.setValue("");
26794             for (var i =0; i < ans.length;i++) {
26795                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26796                     // select it..
26797                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26798                     break;
26799                 }
26800             }
26801         }
26802         
26803         
26804         
26805         // hides menus... - so this cant be on a menu...
26806         Roo.bootstrap.MenuMgr.hideAll();
26807         */
26808         Roo.bootstrap.MenuMgr.hideAll();
26809         //this.editorsyncValue();
26810     },
26811     onFirstFocus: function() {
26812         this.buttons.each(function(item){
26813            item.enable();
26814         });
26815     },
26816     toggleSourceEdit : function(sourceEditMode){
26817         
26818           
26819         if(sourceEditMode){
26820             Roo.log("disabling buttons");
26821            this.buttons.each( function(item){
26822                 if(item.cmd != 'pencil'){
26823                     item.disable();
26824                 }
26825             });
26826           
26827         }else{
26828             Roo.log("enabling buttons");
26829             if(this.editorcore.initialized){
26830                 this.buttons.each( function(item){
26831                     item.enable();
26832                 });
26833             }
26834             
26835         }
26836         Roo.log("calling toggole on editor");
26837         // tell the editor that it's been pressed..
26838         this.editor.toggleSourceEdit(sourceEditMode);
26839        
26840     }
26841 });
26842
26843
26844
26845
26846  
26847 /*
26848  * - LGPL
26849  */
26850
26851 /**
26852  * @class Roo.bootstrap.Markdown
26853  * @extends Roo.bootstrap.TextArea
26854  * Bootstrap Showdown editable area
26855  * @cfg {string} content
26856  * 
26857  * @constructor
26858  * Create a new Showdown
26859  */
26860
26861 Roo.bootstrap.Markdown = function(config){
26862     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26863    
26864 };
26865
26866 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26867     
26868     editing :false,
26869     
26870     initEvents : function()
26871     {
26872         
26873         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26874         this.markdownEl = this.el.createChild({
26875             cls : 'roo-markdown-area'
26876         });
26877         this.inputEl().addClass('d-none');
26878         if (this.getValue() == '') {
26879             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26880             
26881         } else {
26882             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26883         }
26884         this.markdownEl.on('click', this.toggleTextEdit, this);
26885         this.on('blur', this.toggleTextEdit, this);
26886         this.on('specialkey', this.resizeTextArea, this);
26887     },
26888     
26889     toggleTextEdit : function()
26890     {
26891         var sh = this.markdownEl.getHeight();
26892         this.inputEl().addClass('d-none');
26893         this.markdownEl.addClass('d-none');
26894         if (!this.editing) {
26895             // show editor?
26896             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26897             this.inputEl().removeClass('d-none');
26898             this.inputEl().focus();
26899             this.editing = true;
26900             return;
26901         }
26902         // show showdown...
26903         this.updateMarkdown();
26904         this.markdownEl.removeClass('d-none');
26905         this.editing = false;
26906         return;
26907     },
26908     updateMarkdown : function()
26909     {
26910         if (this.getValue() == '') {
26911             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26912             return;
26913         }
26914  
26915         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26916     },
26917     
26918     resizeTextArea: function () {
26919         
26920         var sh = 100;
26921         Roo.log([sh, this.getValue().split("\n").length * 30]);
26922         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26923     },
26924     setValue : function(val)
26925     {
26926         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26927         if (!this.editing) {
26928             this.updateMarkdown();
26929         }
26930         
26931     },
26932     focus : function()
26933     {
26934         if (!this.editing) {
26935             this.toggleTextEdit();
26936         }
26937         
26938     }
26939
26940
26941 });
26942 /**
26943  * @class Roo.bootstrap.Table.AbstractSelectionModel
26944  * @extends Roo.util.Observable
26945  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26946  * implemented by descendant classes.  This class should not be directly instantiated.
26947  * @constructor
26948  */
26949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26950     this.locked = false;
26951     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26952 };
26953
26954
26955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26956     /** @ignore Called by the grid automatically. Do not call directly. */
26957     init : function(grid){
26958         this.grid = grid;
26959         this.initEvents();
26960     },
26961
26962     /**
26963      * Locks the selections.
26964      */
26965     lock : function(){
26966         this.locked = true;
26967     },
26968
26969     /**
26970      * Unlocks the selections.
26971      */
26972     unlock : function(){
26973         this.locked = false;
26974     },
26975
26976     /**
26977      * Returns true if the selections are locked.
26978      * @return {Boolean}
26979      */
26980     isLocked : function(){
26981         return this.locked;
26982     },
26983     
26984     
26985     initEvents : function ()
26986     {
26987         
26988     }
26989 });
26990 /**
26991  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26992  * @class Roo.bootstrap.Table.RowSelectionModel
26993  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26994  * It supports multiple selections and keyboard selection/navigation. 
26995  * @constructor
26996  * @param {Object} config
26997  */
26998
26999 Roo.bootstrap.Table.RowSelectionModel = function(config){
27000     Roo.apply(this, config);
27001     this.selections = new Roo.util.MixedCollection(false, function(o){
27002         return o.id;
27003     });
27004
27005     this.last = false;
27006     this.lastActive = false;
27007
27008     this.addEvents({
27009         /**
27010              * @event selectionchange
27011              * Fires when the selection changes
27012              * @param {SelectionModel} this
27013              */
27014             "selectionchange" : true,
27015         /**
27016              * @event afterselectionchange
27017              * Fires after the selection changes (eg. by key press or clicking)
27018              * @param {SelectionModel} this
27019              */
27020             "afterselectionchange" : true,
27021         /**
27022              * @event beforerowselect
27023              * Fires when a row is selected being selected, return false to cancel.
27024              * @param {SelectionModel} this
27025              * @param {Number} rowIndex The selected index
27026              * @param {Boolean} keepExisting False if other selections will be cleared
27027              */
27028             "beforerowselect" : true,
27029         /**
27030              * @event rowselect
27031              * Fires when a row is selected.
27032              * @param {SelectionModel} this
27033              * @param {Number} rowIndex The selected index
27034              * @param {Roo.data.Record} r The record
27035              */
27036             "rowselect" : true,
27037         /**
27038              * @event rowdeselect
27039              * Fires when a row is deselected.
27040              * @param {SelectionModel} this
27041              * @param {Number} rowIndex The selected index
27042              */
27043         "rowdeselect" : true
27044     });
27045     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27046     this.locked = false;
27047  };
27048
27049 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27050     /**
27051      * @cfg {Boolean} singleSelect
27052      * True to allow selection of only one row at a time (defaults to false)
27053      */
27054     singleSelect : false,
27055
27056     // private
27057     initEvents : function()
27058     {
27059
27060         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27061         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27062         //}else{ // allow click to work like normal
27063          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27064         //}
27065         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27066         this.grid.on("rowclick", this.handleMouseDown, this);
27067         
27068         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27069             "up" : function(e){
27070                 if(!e.shiftKey){
27071                     this.selectPrevious(e.shiftKey);
27072                 }else if(this.last !== false && this.lastActive !== false){
27073                     var last = this.last;
27074                     this.selectRange(this.last,  this.lastActive-1);
27075                     this.grid.getView().focusRow(this.lastActive);
27076                     if(last !== false){
27077                         this.last = last;
27078                     }
27079                 }else{
27080                     this.selectFirstRow();
27081                 }
27082                 this.fireEvent("afterselectionchange", this);
27083             },
27084             "down" : function(e){
27085                 if(!e.shiftKey){
27086                     this.selectNext(e.shiftKey);
27087                 }else if(this.last !== false && this.lastActive !== false){
27088                     var last = this.last;
27089                     this.selectRange(this.last,  this.lastActive+1);
27090                     this.grid.getView().focusRow(this.lastActive);
27091                     if(last !== false){
27092                         this.last = last;
27093                     }
27094                 }else{
27095                     this.selectFirstRow();
27096                 }
27097                 this.fireEvent("afterselectionchange", this);
27098             },
27099             scope: this
27100         });
27101         this.grid.store.on('load', function(){
27102             this.selections.clear();
27103         },this);
27104         /*
27105         var view = this.grid.view;
27106         view.on("refresh", this.onRefresh, this);
27107         view.on("rowupdated", this.onRowUpdated, this);
27108         view.on("rowremoved", this.onRemove, this);
27109         */
27110     },
27111
27112     // private
27113     onRefresh : function()
27114     {
27115         var ds = this.grid.store, i, v = this.grid.view;
27116         var s = this.selections;
27117         s.each(function(r){
27118             if((i = ds.indexOfId(r.id)) != -1){
27119                 v.onRowSelect(i);
27120             }else{
27121                 s.remove(r);
27122             }
27123         });
27124     },
27125
27126     // private
27127     onRemove : function(v, index, r){
27128         this.selections.remove(r);
27129     },
27130
27131     // private
27132     onRowUpdated : function(v, index, r){
27133         if(this.isSelected(r)){
27134             v.onRowSelect(index);
27135         }
27136     },
27137
27138     /**
27139      * Select records.
27140      * @param {Array} records The records to select
27141      * @param {Boolean} keepExisting (optional) True to keep existing selections
27142      */
27143     selectRecords : function(records, keepExisting)
27144     {
27145         if(!keepExisting){
27146             this.clearSelections();
27147         }
27148             var ds = this.grid.store;
27149         for(var i = 0, len = records.length; i < len; i++){
27150             this.selectRow(ds.indexOf(records[i]), true);
27151         }
27152     },
27153
27154     /**
27155      * Gets the number of selected rows.
27156      * @return {Number}
27157      */
27158     getCount : function(){
27159         return this.selections.length;
27160     },
27161
27162     /**
27163      * Selects the first row in the grid.
27164      */
27165     selectFirstRow : function(){
27166         this.selectRow(0);
27167     },
27168
27169     /**
27170      * Select the last row.
27171      * @param {Boolean} keepExisting (optional) True to keep existing selections
27172      */
27173     selectLastRow : function(keepExisting){
27174         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27175         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27176     },
27177
27178     /**
27179      * Selects the row immediately following the last selected row.
27180      * @param {Boolean} keepExisting (optional) True to keep existing selections
27181      */
27182     selectNext : function(keepExisting)
27183     {
27184             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27185             this.selectRow(this.last+1, keepExisting);
27186             this.grid.getView().focusRow(this.last);
27187         }
27188     },
27189
27190     /**
27191      * Selects the row that precedes the last selected row.
27192      * @param {Boolean} keepExisting (optional) True to keep existing selections
27193      */
27194     selectPrevious : function(keepExisting){
27195         if(this.last){
27196             this.selectRow(this.last-1, keepExisting);
27197             this.grid.getView().focusRow(this.last);
27198         }
27199     },
27200
27201     /**
27202      * Returns the selected records
27203      * @return {Array} Array of selected records
27204      */
27205     getSelections : function(){
27206         return [].concat(this.selections.items);
27207     },
27208
27209     /**
27210      * Returns the first selected record.
27211      * @return {Record}
27212      */
27213     getSelected : function(){
27214         return this.selections.itemAt(0);
27215     },
27216
27217
27218     /**
27219      * Clears all selections.
27220      */
27221     clearSelections : function(fast)
27222     {
27223         if(this.locked) {
27224             return;
27225         }
27226         if(fast !== true){
27227                 var ds = this.grid.store;
27228             var s = this.selections;
27229             s.each(function(r){
27230                 this.deselectRow(ds.indexOfId(r.id));
27231             }, this);
27232             s.clear();
27233         }else{
27234             this.selections.clear();
27235         }
27236         this.last = false;
27237     },
27238
27239
27240     /**
27241      * Selects all rows.
27242      */
27243     selectAll : function(){
27244         if(this.locked) {
27245             return;
27246         }
27247         this.selections.clear();
27248         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27249             this.selectRow(i, true);
27250         }
27251     },
27252
27253     /**
27254      * Returns True if there is a selection.
27255      * @return {Boolean}
27256      */
27257     hasSelection : function(){
27258         return this.selections.length > 0;
27259     },
27260
27261     /**
27262      * Returns True if the specified row is selected.
27263      * @param {Number/Record} record The record or index of the record to check
27264      * @return {Boolean}
27265      */
27266     isSelected : function(index){
27267             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27268         return (r && this.selections.key(r.id) ? true : false);
27269     },
27270
27271     /**
27272      * Returns True if the specified record id is selected.
27273      * @param {String} id The id of record to check
27274      * @return {Boolean}
27275      */
27276     isIdSelected : function(id){
27277         return (this.selections.key(id) ? true : false);
27278     },
27279
27280
27281     // private
27282     handleMouseDBClick : function(e, t){
27283         
27284     },
27285     // private
27286     handleMouseDown : function(e, t)
27287     {
27288             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27289         if(this.isLocked() || rowIndex < 0 ){
27290             return;
27291         };
27292         if(e.shiftKey && this.last !== false){
27293             var last = this.last;
27294             this.selectRange(last, rowIndex, e.ctrlKey);
27295             this.last = last; // reset the last
27296             t.focus();
27297     
27298         }else{
27299             var isSelected = this.isSelected(rowIndex);
27300             //Roo.log("select row:" + rowIndex);
27301             if(isSelected){
27302                 this.deselectRow(rowIndex);
27303             } else {
27304                         this.selectRow(rowIndex, true);
27305             }
27306     
27307             /*
27308                 if(e.button !== 0 && isSelected){
27309                 alert('rowIndex 2: ' + rowIndex);
27310                     view.focusRow(rowIndex);
27311                 }else if(e.ctrlKey && isSelected){
27312                     this.deselectRow(rowIndex);
27313                 }else if(!isSelected){
27314                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27315                     view.focusRow(rowIndex);
27316                 }
27317             */
27318         }
27319         this.fireEvent("afterselectionchange", this);
27320     },
27321     // private
27322     handleDragableRowClick :  function(grid, rowIndex, e) 
27323     {
27324         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27325             this.selectRow(rowIndex, false);
27326             grid.view.focusRow(rowIndex);
27327              this.fireEvent("afterselectionchange", this);
27328         }
27329     },
27330     
27331     /**
27332      * Selects multiple rows.
27333      * @param {Array} rows Array of the indexes of the row to select
27334      * @param {Boolean} keepExisting (optional) True to keep existing selections
27335      */
27336     selectRows : function(rows, keepExisting){
27337         if(!keepExisting){
27338             this.clearSelections();
27339         }
27340         for(var i = 0, len = rows.length; i < len; i++){
27341             this.selectRow(rows[i], true);
27342         }
27343     },
27344
27345     /**
27346      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27347      * @param {Number} startRow The index of the first row in the range
27348      * @param {Number} endRow The index of the last row in the range
27349      * @param {Boolean} keepExisting (optional) True to retain existing selections
27350      */
27351     selectRange : function(startRow, endRow, keepExisting){
27352         if(this.locked) {
27353             return;
27354         }
27355         if(!keepExisting){
27356             this.clearSelections();
27357         }
27358         if(startRow <= endRow){
27359             for(var i = startRow; i <= endRow; i++){
27360                 this.selectRow(i, true);
27361             }
27362         }else{
27363             for(var i = startRow; i >= endRow; i--){
27364                 this.selectRow(i, true);
27365             }
27366         }
27367     },
27368
27369     /**
27370      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27371      * @param {Number} startRow The index of the first row in the range
27372      * @param {Number} endRow The index of the last row in the range
27373      */
27374     deselectRange : function(startRow, endRow, preventViewNotify){
27375         if(this.locked) {
27376             return;
27377         }
27378         for(var i = startRow; i <= endRow; i++){
27379             this.deselectRow(i, preventViewNotify);
27380         }
27381     },
27382
27383     /**
27384      * Selects a row.
27385      * @param {Number} row The index of the row to select
27386      * @param {Boolean} keepExisting (optional) True to keep existing selections
27387      */
27388     selectRow : function(index, keepExisting, preventViewNotify)
27389     {
27390             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27391             return;
27392         }
27393         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27394             if(!keepExisting || this.singleSelect){
27395                 this.clearSelections();
27396             }
27397             
27398             var r = this.grid.store.getAt(index);
27399             //console.log('selectRow - record id :' + r.id);
27400             
27401             this.selections.add(r);
27402             this.last = this.lastActive = index;
27403             if(!preventViewNotify){
27404                 var proxy = new Roo.Element(
27405                                 this.grid.getRowDom(index)
27406                 );
27407                 proxy.addClass('bg-info info');
27408             }
27409             this.fireEvent("rowselect", this, index, r);
27410             this.fireEvent("selectionchange", this);
27411         }
27412     },
27413
27414     /**
27415      * Deselects a row.
27416      * @param {Number} row The index of the row to deselect
27417      */
27418     deselectRow : function(index, preventViewNotify)
27419     {
27420         if(this.locked) {
27421             return;
27422         }
27423         if(this.last == index){
27424             this.last = false;
27425         }
27426         if(this.lastActive == index){
27427             this.lastActive = false;
27428         }
27429         
27430         var r = this.grid.store.getAt(index);
27431         if (!r) {
27432             return;
27433         }
27434         
27435         this.selections.remove(r);
27436         //.console.log('deselectRow - record id :' + r.id);
27437         if(!preventViewNotify){
27438         
27439             var proxy = new Roo.Element(
27440                 this.grid.getRowDom(index)
27441             );
27442             proxy.removeClass('bg-info info');
27443         }
27444         this.fireEvent("rowdeselect", this, index);
27445         this.fireEvent("selectionchange", this);
27446     },
27447
27448     // private
27449     restoreLast : function(){
27450         if(this._last){
27451             this.last = this._last;
27452         }
27453     },
27454
27455     // private
27456     acceptsNav : function(row, col, cm){
27457         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27458     },
27459
27460     // private
27461     onEditorKey : function(field, e){
27462         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27463         if(k == e.TAB){
27464             e.stopEvent();
27465             ed.completeEdit();
27466             if(e.shiftKey){
27467                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27468             }else{
27469                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27470             }
27471         }else if(k == e.ENTER && !e.ctrlKey){
27472             e.stopEvent();
27473             ed.completeEdit();
27474             if(e.shiftKey){
27475                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27476             }else{
27477                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27478             }
27479         }else if(k == e.ESC){
27480             ed.cancelEdit();
27481         }
27482         if(newCell){
27483             g.startEditing(newCell[0], newCell[1]);
27484         }
27485     }
27486 });
27487 /*
27488  * Based on:
27489  * Ext JS Library 1.1.1
27490  * Copyright(c) 2006-2007, Ext JS, LLC.
27491  *
27492  * Originally Released Under LGPL - original licence link has changed is not relivant.
27493  *
27494  * Fork - LGPL
27495  * <script type="text/javascript">
27496  */
27497  
27498 /**
27499  * @class Roo.bootstrap.PagingToolbar
27500  * @extends Roo.bootstrap.NavSimplebar
27501  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27502  * @constructor
27503  * Create a new PagingToolbar
27504  * @param {Object} config The config object
27505  * @param {Roo.data.Store} store
27506  */
27507 Roo.bootstrap.PagingToolbar = function(config)
27508 {
27509     // old args format still supported... - xtype is prefered..
27510         // created from xtype...
27511     
27512     this.ds = config.dataSource;
27513     
27514     if (config.store && !this.ds) {
27515         this.store= Roo.factory(config.store, Roo.data);
27516         this.ds = this.store;
27517         this.ds.xmodule = this.xmodule || false;
27518     }
27519     
27520     this.toolbarItems = [];
27521     if (config.items) {
27522         this.toolbarItems = config.items;
27523     }
27524     
27525     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27526     
27527     this.cursor = 0;
27528     
27529     if (this.ds) { 
27530         this.bind(this.ds);
27531     }
27532     
27533     if (Roo.bootstrap.version == 4) {
27534         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27535     } else {
27536         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27537     }
27538     
27539 };
27540
27541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27542     /**
27543      * @cfg {Roo.data.Store} dataSource
27544      * The underlying data store providing the paged data
27545      */
27546     /**
27547      * @cfg {String/HTMLElement/Element} container
27548      * container The id or element that will contain the toolbar
27549      */
27550     /**
27551      * @cfg {Boolean} displayInfo
27552      * True to display the displayMsg (defaults to false)
27553      */
27554     /**
27555      * @cfg {Number} pageSize
27556      * The number of records to display per page (defaults to 20)
27557      */
27558     pageSize: 20,
27559     /**
27560      * @cfg {String} displayMsg
27561      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27562      */
27563     displayMsg : 'Displaying {0} - {1} of {2}',
27564     /**
27565      * @cfg {String} emptyMsg
27566      * The message to display when no records are found (defaults to "No data to display")
27567      */
27568     emptyMsg : 'No data to display',
27569     /**
27570      * Customizable piece of the default paging text (defaults to "Page")
27571      * @type String
27572      */
27573     beforePageText : "Page",
27574     /**
27575      * Customizable piece of the default paging text (defaults to "of %0")
27576      * @type String
27577      */
27578     afterPageText : "of {0}",
27579     /**
27580      * Customizable piece of the default paging text (defaults to "First Page")
27581      * @type String
27582      */
27583     firstText : "First Page",
27584     /**
27585      * Customizable piece of the default paging text (defaults to "Previous Page")
27586      * @type String
27587      */
27588     prevText : "Previous Page",
27589     /**
27590      * Customizable piece of the default paging text (defaults to "Next Page")
27591      * @type String
27592      */
27593     nextText : "Next Page",
27594     /**
27595      * Customizable piece of the default paging text (defaults to "Last Page")
27596      * @type String
27597      */
27598     lastText : "Last Page",
27599     /**
27600      * Customizable piece of the default paging text (defaults to "Refresh")
27601      * @type String
27602      */
27603     refreshText : "Refresh",
27604
27605     buttons : false,
27606     // private
27607     onRender : function(ct, position) 
27608     {
27609         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27610         this.navgroup.parentId = this.id;
27611         this.navgroup.onRender(this.el, null);
27612         // add the buttons to the navgroup
27613         
27614         if(this.displayInfo){
27615             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27616             this.displayEl = this.el.select('.x-paging-info', true).first();
27617 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27618 //            this.displayEl = navel.el.select('span',true).first();
27619         }
27620         
27621         var _this = this;
27622         
27623         if(this.buttons){
27624             Roo.each(_this.buttons, function(e){ // this might need to use render????
27625                Roo.factory(e).render(_this.el);
27626             });
27627         }
27628             
27629         Roo.each(_this.toolbarItems, function(e) {
27630             _this.navgroup.addItem(e);
27631         });
27632         
27633         
27634         this.first = this.navgroup.addItem({
27635             tooltip: this.firstText,
27636             cls: "prev btn-outline-secondary",
27637             html : ' <i class="fa fa-step-backward"></i>',
27638             disabled: true,
27639             preventDefault: true,
27640             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27641         });
27642         
27643         this.prev =  this.navgroup.addItem({
27644             tooltip: this.prevText,
27645             cls: "prev btn-outline-secondary",
27646             html : ' <i class="fa fa-backward"></i>',
27647             disabled: true,
27648             preventDefault: true,
27649             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27650         });
27651     //this.addSeparator();
27652         
27653         
27654         var field = this.navgroup.addItem( {
27655             tagtype : 'span',
27656             cls : 'x-paging-position  btn-outline-secondary',
27657              disabled: true,
27658             html : this.beforePageText  +
27659                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27660                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27661          } ); //?? escaped?
27662         
27663         this.field = field.el.select('input', true).first();
27664         this.field.on("keydown", this.onPagingKeydown, this);
27665         this.field.on("focus", function(){this.dom.select();});
27666     
27667     
27668         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27669         //this.field.setHeight(18);
27670         //this.addSeparator();
27671         this.next = this.navgroup.addItem({
27672             tooltip: this.nextText,
27673             cls: "next btn-outline-secondary",
27674             html : ' <i class="fa fa-forward"></i>',
27675             disabled: true,
27676             preventDefault: true,
27677             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27678         });
27679         this.last = this.navgroup.addItem({
27680             tooltip: this.lastText,
27681             html : ' <i class="fa fa-step-forward"></i>',
27682             cls: "next btn-outline-secondary",
27683             disabled: true,
27684             preventDefault: true,
27685             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27686         });
27687     //this.addSeparator();
27688         this.loading = this.navgroup.addItem({
27689             tooltip: this.refreshText,
27690             cls: "btn-outline-secondary",
27691             html : ' <i class="fa fa-refresh"></i>',
27692             preventDefault: true,
27693             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27694         });
27695         
27696     },
27697
27698     // private
27699     updateInfo : function(){
27700         if(this.displayEl){
27701             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27702             var msg = count == 0 ?
27703                 this.emptyMsg :
27704                 String.format(
27705                     this.displayMsg,
27706                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27707                 );
27708             this.displayEl.update(msg);
27709         }
27710     },
27711
27712     // private
27713     onLoad : function(ds, r, o)
27714     {
27715         this.cursor = o.params && o.params.start ? o.params.start : 0;
27716         
27717         var d = this.getPageData(),
27718             ap = d.activePage,
27719             ps = d.pages;
27720         
27721         
27722         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27723         this.field.dom.value = ap;
27724         this.first.setDisabled(ap == 1);
27725         this.prev.setDisabled(ap == 1);
27726         this.next.setDisabled(ap == ps);
27727         this.last.setDisabled(ap == ps);
27728         this.loading.enable();
27729         this.updateInfo();
27730     },
27731
27732     // private
27733     getPageData : function(){
27734         var total = this.ds.getTotalCount();
27735         return {
27736             total : total,
27737             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27738             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27739         };
27740     },
27741
27742     // private
27743     onLoadError : function(){
27744         this.loading.enable();
27745     },
27746
27747     // private
27748     onPagingKeydown : function(e){
27749         var k = e.getKey();
27750         var d = this.getPageData();
27751         if(k == e.RETURN){
27752             var v = this.field.dom.value, pageNum;
27753             if(!v || isNaN(pageNum = parseInt(v, 10))){
27754                 this.field.dom.value = d.activePage;
27755                 return;
27756             }
27757             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27758             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27759             e.stopEvent();
27760         }
27761         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))
27762         {
27763           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27764           this.field.dom.value = pageNum;
27765           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27766           e.stopEvent();
27767         }
27768         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27769         {
27770           var v = this.field.dom.value, pageNum; 
27771           var increment = (e.shiftKey) ? 10 : 1;
27772           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27773                 increment *= -1;
27774           }
27775           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27776             this.field.dom.value = d.activePage;
27777             return;
27778           }
27779           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27780           {
27781             this.field.dom.value = parseInt(v, 10) + increment;
27782             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27783             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27784           }
27785           e.stopEvent();
27786         }
27787     },
27788
27789     // private
27790     beforeLoad : function(){
27791         if(this.loading){
27792             this.loading.disable();
27793         }
27794     },
27795
27796     // private
27797     onClick : function(which){
27798         
27799         var ds = this.ds;
27800         if (!ds) {
27801             return;
27802         }
27803         
27804         switch(which){
27805             case "first":
27806                 ds.load({params:{start: 0, limit: this.pageSize}});
27807             break;
27808             case "prev":
27809                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27810             break;
27811             case "next":
27812                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27813             break;
27814             case "last":
27815                 var total = ds.getTotalCount();
27816                 var extra = total % this.pageSize;
27817                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27818                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27819             break;
27820             case "refresh":
27821                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27822             break;
27823         }
27824     },
27825
27826     /**
27827      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27828      * @param {Roo.data.Store} store The data store to unbind
27829      */
27830     unbind : function(ds){
27831         ds.un("beforeload", this.beforeLoad, this);
27832         ds.un("load", this.onLoad, this);
27833         ds.un("loadexception", this.onLoadError, this);
27834         ds.un("remove", this.updateInfo, this);
27835         ds.un("add", this.updateInfo, this);
27836         this.ds = undefined;
27837     },
27838
27839     /**
27840      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27841      * @param {Roo.data.Store} store The data store to bind
27842      */
27843     bind : function(ds){
27844         ds.on("beforeload", this.beforeLoad, this);
27845         ds.on("load", this.onLoad, this);
27846         ds.on("loadexception", this.onLoadError, this);
27847         ds.on("remove", this.updateInfo, this);
27848         ds.on("add", this.updateInfo, this);
27849         this.ds = ds;
27850     }
27851 });/*
27852  * - LGPL
27853  *
27854  * element
27855  * 
27856  */
27857
27858 /**
27859  * @class Roo.bootstrap.MessageBar
27860  * @extends Roo.bootstrap.Component
27861  * Bootstrap MessageBar class
27862  * @cfg {String} html contents of the MessageBar
27863  * @cfg {String} weight (info | success | warning | danger) default info
27864  * @cfg {String} beforeClass insert the bar before the given class
27865  * @cfg {Boolean} closable (true | false) default false
27866  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27867  * 
27868  * @constructor
27869  * Create a new Element
27870  * @param {Object} config The config object
27871  */
27872
27873 Roo.bootstrap.MessageBar = function(config){
27874     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27875 };
27876
27877 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27878     
27879     html: '',
27880     weight: 'info',
27881     closable: false,
27882     fixed: false,
27883     beforeClass: 'bootstrap-sticky-wrap',
27884     
27885     getAutoCreate : function(){
27886         
27887         var cfg = {
27888             tag: 'div',
27889             cls: 'alert alert-dismissable alert-' + this.weight,
27890             cn: [
27891                 {
27892                     tag: 'span',
27893                     cls: 'message',
27894                     html: this.html || ''
27895                 }
27896             ]
27897         };
27898         
27899         if(this.fixed){
27900             cfg.cls += ' alert-messages-fixed';
27901         }
27902         
27903         if(this.closable){
27904             cfg.cn.push({
27905                 tag: 'button',
27906                 cls: 'close',
27907                 html: 'x'
27908             });
27909         }
27910         
27911         return cfg;
27912     },
27913     
27914     onRender : function(ct, position)
27915     {
27916         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27917         
27918         if(!this.el){
27919             var cfg = Roo.apply({},  this.getAutoCreate());
27920             cfg.id = Roo.id();
27921             
27922             if (this.cls) {
27923                 cfg.cls += ' ' + this.cls;
27924             }
27925             if (this.style) {
27926                 cfg.style = this.style;
27927             }
27928             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27929             
27930             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27931         }
27932         
27933         this.el.select('>button.close').on('click', this.hide, this);
27934         
27935     },
27936     
27937     show : function()
27938     {
27939         if (!this.rendered) {
27940             this.render();
27941         }
27942         
27943         this.el.show();
27944         
27945         this.fireEvent('show', this);
27946         
27947     },
27948     
27949     hide : function()
27950     {
27951         if (!this.rendered) {
27952             this.render();
27953         }
27954         
27955         this.el.hide();
27956         
27957         this.fireEvent('hide', this);
27958     },
27959     
27960     update : function()
27961     {
27962 //        var e = this.el.dom.firstChild;
27963 //        
27964 //        if(this.closable){
27965 //            e = e.nextSibling;
27966 //        }
27967 //        
27968 //        e.data = this.html || '';
27969
27970         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27971     }
27972    
27973 });
27974
27975  
27976
27977      /*
27978  * - LGPL
27979  *
27980  * Graph
27981  * 
27982  */
27983
27984
27985 /**
27986  * @class Roo.bootstrap.Graph
27987  * @extends Roo.bootstrap.Component
27988  * Bootstrap Graph class
27989 > Prameters
27990  -sm {number} sm 4
27991  -md {number} md 5
27992  @cfg {String} graphtype  bar | vbar | pie
27993  @cfg {number} g_x coodinator | centre x (pie)
27994  @cfg {number} g_y coodinator | centre y (pie)
27995  @cfg {number} g_r radius (pie)
27996  @cfg {number} g_height height of the chart (respected by all elements in the set)
27997  @cfg {number} g_width width of the chart (respected by all elements in the set)
27998  @cfg {Object} title The title of the chart
27999     
28000  -{Array}  values
28001  -opts (object) options for the chart 
28002      o {
28003      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28004      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28005      o vgutter (number)
28006      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.
28007      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28008      o to
28009      o stretch (boolean)
28010      o }
28011  -opts (object) options for the pie
28012      o{
28013      o cut
28014      o startAngle (number)
28015      o endAngle (number)
28016      } 
28017  *
28018  * @constructor
28019  * Create a new Input
28020  * @param {Object} config The config object
28021  */
28022
28023 Roo.bootstrap.Graph = function(config){
28024     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28025     
28026     this.addEvents({
28027         // img events
28028         /**
28029          * @event click
28030          * The img click event for the img.
28031          * @param {Roo.EventObject} e
28032          */
28033         "click" : true
28034     });
28035 };
28036
28037 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28038     
28039     sm: 4,
28040     md: 5,
28041     graphtype: 'bar',
28042     g_height: 250,
28043     g_width: 400,
28044     g_x: 50,
28045     g_y: 50,
28046     g_r: 30,
28047     opts:{
28048         //g_colors: this.colors,
28049         g_type: 'soft',
28050         g_gutter: '20%'
28051
28052     },
28053     title : false,
28054
28055     getAutoCreate : function(){
28056         
28057         var cfg = {
28058             tag: 'div',
28059             html : null
28060         };
28061         
28062         
28063         return  cfg;
28064     },
28065
28066     onRender : function(ct,position){
28067         
28068         
28069         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28070         
28071         if (typeof(Raphael) == 'undefined') {
28072             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28073             return;
28074         }
28075         
28076         this.raphael = Raphael(this.el.dom);
28077         
28078                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28079                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28080                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28081                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28082                 /*
28083                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28084                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28085                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28086                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28087                 
28088                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28089                 r.barchart(330, 10, 300, 220, data1);
28090                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28091                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28092                 */
28093                 
28094                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28095                 // r.barchart(30, 30, 560, 250,  xdata, {
28096                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28097                 //     axis : "0 0 1 1",
28098                 //     axisxlabels :  xdata
28099                 //     //yvalues : cols,
28100                    
28101                 // });
28102 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28103 //        
28104 //        this.load(null,xdata,{
28105 //                axis : "0 0 1 1",
28106 //                axisxlabels :  xdata
28107 //                });
28108
28109     },
28110
28111     load : function(graphtype,xdata,opts)
28112     {
28113         this.raphael.clear();
28114         if(!graphtype) {
28115             graphtype = this.graphtype;
28116         }
28117         if(!opts){
28118             opts = this.opts;
28119         }
28120         var r = this.raphael,
28121             fin = function () {
28122                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28123             },
28124             fout = function () {
28125                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28126             },
28127             pfin = function() {
28128                 this.sector.stop();
28129                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28130
28131                 if (this.label) {
28132                     this.label[0].stop();
28133                     this.label[0].attr({ r: 7.5 });
28134                     this.label[1].attr({ "font-weight": 800 });
28135                 }
28136             },
28137             pfout = function() {
28138                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28139
28140                 if (this.label) {
28141                     this.label[0].animate({ r: 5 }, 500, "bounce");
28142                     this.label[1].attr({ "font-weight": 400 });
28143                 }
28144             };
28145
28146         switch(graphtype){
28147             case 'bar':
28148                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28149                 break;
28150             case 'hbar':
28151                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28152                 break;
28153             case 'pie':
28154 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28155 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28156 //            
28157                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28158                 
28159                 break;
28160
28161         }
28162         
28163         if(this.title){
28164             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28165         }
28166         
28167     },
28168     
28169     setTitle: function(o)
28170     {
28171         this.title = o;
28172     },
28173     
28174     initEvents: function() {
28175         
28176         if(!this.href){
28177             this.el.on('click', this.onClick, this);
28178         }
28179     },
28180     
28181     onClick : function(e)
28182     {
28183         Roo.log('img onclick');
28184         this.fireEvent('click', this, e);
28185     }
28186    
28187 });
28188
28189  
28190 /*
28191  * - LGPL
28192  *
28193  * numberBox
28194  * 
28195  */
28196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28197
28198 /**
28199  * @class Roo.bootstrap.dash.NumberBox
28200  * @extends Roo.bootstrap.Component
28201  * Bootstrap NumberBox class
28202  * @cfg {String} headline Box headline
28203  * @cfg {String} content Box content
28204  * @cfg {String} icon Box icon
28205  * @cfg {String} footer Footer text
28206  * @cfg {String} fhref Footer href
28207  * 
28208  * @constructor
28209  * Create a new NumberBox
28210  * @param {Object} config The config object
28211  */
28212
28213
28214 Roo.bootstrap.dash.NumberBox = function(config){
28215     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28216     
28217 };
28218
28219 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28220     
28221     headline : '',
28222     content : '',
28223     icon : '',
28224     footer : '',
28225     fhref : '',
28226     ficon : '',
28227     
28228     getAutoCreate : function(){
28229         
28230         var cfg = {
28231             tag : 'div',
28232             cls : 'small-box ',
28233             cn : [
28234                 {
28235                     tag : 'div',
28236                     cls : 'inner',
28237                     cn :[
28238                         {
28239                             tag : 'h3',
28240                             cls : 'roo-headline',
28241                             html : this.headline
28242                         },
28243                         {
28244                             tag : 'p',
28245                             cls : 'roo-content',
28246                             html : this.content
28247                         }
28248                     ]
28249                 }
28250             ]
28251         };
28252         
28253         if(this.icon){
28254             cfg.cn.push({
28255                 tag : 'div',
28256                 cls : 'icon',
28257                 cn :[
28258                     {
28259                         tag : 'i',
28260                         cls : 'ion ' + this.icon
28261                     }
28262                 ]
28263             });
28264         }
28265         
28266         if(this.footer){
28267             var footer = {
28268                 tag : 'a',
28269                 cls : 'small-box-footer',
28270                 href : this.fhref || '#',
28271                 html : this.footer
28272             };
28273             
28274             cfg.cn.push(footer);
28275             
28276         }
28277         
28278         return  cfg;
28279     },
28280
28281     onRender : function(ct,position){
28282         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28283
28284
28285        
28286                 
28287     },
28288
28289     setHeadline: function (value)
28290     {
28291         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28292     },
28293     
28294     setFooter: function (value, href)
28295     {
28296         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28297         
28298         if(href){
28299             this.el.select('a.small-box-footer',true).first().attr('href', href);
28300         }
28301         
28302     },
28303
28304     setContent: function (value)
28305     {
28306         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28307     },
28308
28309     initEvents: function() 
28310     {   
28311         
28312     }
28313     
28314 });
28315
28316  
28317 /*
28318  * - LGPL
28319  *
28320  * TabBox
28321  * 
28322  */
28323 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28324
28325 /**
28326  * @class Roo.bootstrap.dash.TabBox
28327  * @extends Roo.bootstrap.Component
28328  * Bootstrap TabBox class
28329  * @cfg {String} title Title of the TabBox
28330  * @cfg {String} icon Icon of the TabBox
28331  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28332  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28333  * 
28334  * @constructor
28335  * Create a new TabBox
28336  * @param {Object} config The config object
28337  */
28338
28339
28340 Roo.bootstrap.dash.TabBox = function(config){
28341     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28342     this.addEvents({
28343         // raw events
28344         /**
28345          * @event addpane
28346          * When a pane is added
28347          * @param {Roo.bootstrap.dash.TabPane} pane
28348          */
28349         "addpane" : true,
28350         /**
28351          * @event activatepane
28352          * When a pane is activated
28353          * @param {Roo.bootstrap.dash.TabPane} pane
28354          */
28355         "activatepane" : true
28356         
28357          
28358     });
28359     
28360     this.panes = [];
28361 };
28362
28363 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28364
28365     title : '',
28366     icon : false,
28367     showtabs : true,
28368     tabScrollable : false,
28369     
28370     getChildContainer : function()
28371     {
28372         return this.el.select('.tab-content', true).first();
28373     },
28374     
28375     getAutoCreate : function(){
28376         
28377         var header = {
28378             tag: 'li',
28379             cls: 'pull-left header',
28380             html: this.title,
28381             cn : []
28382         };
28383         
28384         if(this.icon){
28385             header.cn.push({
28386                 tag: 'i',
28387                 cls: 'fa ' + this.icon
28388             });
28389         }
28390         
28391         var h = {
28392             tag: 'ul',
28393             cls: 'nav nav-tabs pull-right',
28394             cn: [
28395                 header
28396             ]
28397         };
28398         
28399         if(this.tabScrollable){
28400             h = {
28401                 tag: 'div',
28402                 cls: 'tab-header',
28403                 cn: [
28404                     {
28405                         tag: 'ul',
28406                         cls: 'nav nav-tabs pull-right',
28407                         cn: [
28408                             header
28409                         ]
28410                     }
28411                 ]
28412             };
28413         }
28414         
28415         var cfg = {
28416             tag: 'div',
28417             cls: 'nav-tabs-custom',
28418             cn: [
28419                 h,
28420                 {
28421                     tag: 'div',
28422                     cls: 'tab-content no-padding',
28423                     cn: []
28424                 }
28425             ]
28426         };
28427
28428         return  cfg;
28429     },
28430     initEvents : function()
28431     {
28432         //Roo.log('add add pane handler');
28433         this.on('addpane', this.onAddPane, this);
28434     },
28435      /**
28436      * Updates the box title
28437      * @param {String} html to set the title to.
28438      */
28439     setTitle : function(value)
28440     {
28441         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28442     },
28443     onAddPane : function(pane)
28444     {
28445         this.panes.push(pane);
28446         //Roo.log('addpane');
28447         //Roo.log(pane);
28448         // tabs are rendere left to right..
28449         if(!this.showtabs){
28450             return;
28451         }
28452         
28453         var ctr = this.el.select('.nav-tabs', true).first();
28454          
28455          
28456         var existing = ctr.select('.nav-tab',true);
28457         var qty = existing.getCount();;
28458         
28459         
28460         var tab = ctr.createChild({
28461             tag : 'li',
28462             cls : 'nav-tab' + (qty ? '' : ' active'),
28463             cn : [
28464                 {
28465                     tag : 'a',
28466                     href:'#',
28467                     html : pane.title
28468                 }
28469             ]
28470         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28471         pane.tab = tab;
28472         
28473         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28474         if (!qty) {
28475             pane.el.addClass('active');
28476         }
28477         
28478                 
28479     },
28480     onTabClick : function(ev,un,ob,pane)
28481     {
28482         //Roo.log('tab - prev default');
28483         ev.preventDefault();
28484         
28485         
28486         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28487         pane.tab.addClass('active');
28488         //Roo.log(pane.title);
28489         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28490         // technically we should have a deactivate event.. but maybe add later.
28491         // and it should not de-activate the selected tab...
28492         this.fireEvent('activatepane', pane);
28493         pane.el.addClass('active');
28494         pane.fireEvent('activate');
28495         
28496         
28497     },
28498     
28499     getActivePane : function()
28500     {
28501         var r = false;
28502         Roo.each(this.panes, function(p) {
28503             if(p.el.hasClass('active')){
28504                 r = p;
28505                 return false;
28506             }
28507             
28508             return;
28509         });
28510         
28511         return r;
28512     }
28513     
28514     
28515 });
28516
28517  
28518 /*
28519  * - LGPL
28520  *
28521  * Tab pane
28522  * 
28523  */
28524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28525 /**
28526  * @class Roo.bootstrap.TabPane
28527  * @extends Roo.bootstrap.Component
28528  * Bootstrap TabPane class
28529  * @cfg {Boolean} active (false | true) Default false
28530  * @cfg {String} title title of panel
28531
28532  * 
28533  * @constructor
28534  * Create a new TabPane
28535  * @param {Object} config The config object
28536  */
28537
28538 Roo.bootstrap.dash.TabPane = function(config){
28539     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28540     
28541     this.addEvents({
28542         // raw events
28543         /**
28544          * @event activate
28545          * When a pane is activated
28546          * @param {Roo.bootstrap.dash.TabPane} pane
28547          */
28548         "activate" : true
28549          
28550     });
28551 };
28552
28553 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28554     
28555     active : false,
28556     title : '',
28557     
28558     // the tabBox that this is attached to.
28559     tab : false,
28560      
28561     getAutoCreate : function() 
28562     {
28563         var cfg = {
28564             tag: 'div',
28565             cls: 'tab-pane'
28566         };
28567         
28568         if(this.active){
28569             cfg.cls += ' active';
28570         }
28571         
28572         return cfg;
28573     },
28574     initEvents  : function()
28575     {
28576         //Roo.log('trigger add pane handler');
28577         this.parent().fireEvent('addpane', this)
28578     },
28579     
28580      /**
28581      * Updates the tab title 
28582      * @param {String} html to set the title to.
28583      */
28584     setTitle: function(str)
28585     {
28586         if (!this.tab) {
28587             return;
28588         }
28589         this.title = str;
28590         this.tab.select('a', true).first().dom.innerHTML = str;
28591         
28592     }
28593     
28594     
28595     
28596 });
28597
28598  
28599
28600
28601  /*
28602  * - LGPL
28603  *
28604  * menu
28605  * 
28606  */
28607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28608
28609 /**
28610  * @class Roo.bootstrap.menu.Menu
28611  * @extends Roo.bootstrap.Component
28612  * Bootstrap Menu class - container for Menu
28613  * @cfg {String} html Text of the menu
28614  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28615  * @cfg {String} icon Font awesome icon
28616  * @cfg {String} pos Menu align to (top | bottom) default bottom
28617  * 
28618  * 
28619  * @constructor
28620  * Create a new Menu
28621  * @param {Object} config The config object
28622  */
28623
28624
28625 Roo.bootstrap.menu.Menu = function(config){
28626     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28627     
28628     this.addEvents({
28629         /**
28630          * @event beforeshow
28631          * Fires before this menu is displayed
28632          * @param {Roo.bootstrap.menu.Menu} this
28633          */
28634         beforeshow : true,
28635         /**
28636          * @event beforehide
28637          * Fires before this menu is hidden
28638          * @param {Roo.bootstrap.menu.Menu} this
28639          */
28640         beforehide : true,
28641         /**
28642          * @event show
28643          * Fires after this menu is displayed
28644          * @param {Roo.bootstrap.menu.Menu} this
28645          */
28646         show : true,
28647         /**
28648          * @event hide
28649          * Fires after this menu is hidden
28650          * @param {Roo.bootstrap.menu.Menu} this
28651          */
28652         hide : true,
28653         /**
28654          * @event click
28655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28656          * @param {Roo.bootstrap.menu.Menu} this
28657          * @param {Roo.EventObject} e
28658          */
28659         click : true
28660     });
28661     
28662 };
28663
28664 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28665     
28666     submenu : false,
28667     html : '',
28668     weight : 'default',
28669     icon : false,
28670     pos : 'bottom',
28671     
28672     
28673     getChildContainer : function() {
28674         if(this.isSubMenu){
28675             return this.el;
28676         }
28677         
28678         return this.el.select('ul.dropdown-menu', true).first();  
28679     },
28680     
28681     getAutoCreate : function()
28682     {
28683         var text = [
28684             {
28685                 tag : 'span',
28686                 cls : 'roo-menu-text',
28687                 html : this.html
28688             }
28689         ];
28690         
28691         if(this.icon){
28692             text.unshift({
28693                 tag : 'i',
28694                 cls : 'fa ' + this.icon
28695             })
28696         }
28697         
28698         
28699         var cfg = {
28700             tag : 'div',
28701             cls : 'btn-group',
28702             cn : [
28703                 {
28704                     tag : 'button',
28705                     cls : 'dropdown-button btn btn-' + this.weight,
28706                     cn : text
28707                 },
28708                 {
28709                     tag : 'button',
28710                     cls : 'dropdown-toggle btn btn-' + this.weight,
28711                     cn : [
28712                         {
28713                             tag : 'span',
28714                             cls : 'caret'
28715                         }
28716                     ]
28717                 },
28718                 {
28719                     tag : 'ul',
28720                     cls : 'dropdown-menu'
28721                 }
28722             ]
28723             
28724         };
28725         
28726         if(this.pos == 'top'){
28727             cfg.cls += ' dropup';
28728         }
28729         
28730         if(this.isSubMenu){
28731             cfg = {
28732                 tag : 'ul',
28733                 cls : 'dropdown-menu'
28734             }
28735         }
28736         
28737         return cfg;
28738     },
28739     
28740     onRender : function(ct, position)
28741     {
28742         this.isSubMenu = ct.hasClass('dropdown-submenu');
28743         
28744         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28745     },
28746     
28747     initEvents : function() 
28748     {
28749         if(this.isSubMenu){
28750             return;
28751         }
28752         
28753         this.hidden = true;
28754         
28755         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28756         this.triggerEl.on('click', this.onTriggerPress, this);
28757         
28758         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28759         this.buttonEl.on('click', this.onClick, this);
28760         
28761     },
28762     
28763     list : function()
28764     {
28765         if(this.isSubMenu){
28766             return this.el;
28767         }
28768         
28769         return this.el.select('ul.dropdown-menu', true).first();
28770     },
28771     
28772     onClick : function(e)
28773     {
28774         this.fireEvent("click", this, e);
28775     },
28776     
28777     onTriggerPress  : function(e)
28778     {   
28779         if (this.isVisible()) {
28780             this.hide();
28781         } else {
28782             this.show();
28783         }
28784     },
28785     
28786     isVisible : function(){
28787         return !this.hidden;
28788     },
28789     
28790     show : function()
28791     {
28792         this.fireEvent("beforeshow", this);
28793         
28794         this.hidden = false;
28795         this.el.addClass('open');
28796         
28797         Roo.get(document).on("mouseup", this.onMouseUp, this);
28798         
28799         this.fireEvent("show", this);
28800         
28801         
28802     },
28803     
28804     hide : function()
28805     {
28806         this.fireEvent("beforehide", this);
28807         
28808         this.hidden = true;
28809         this.el.removeClass('open');
28810         
28811         Roo.get(document).un("mouseup", this.onMouseUp);
28812         
28813         this.fireEvent("hide", this);
28814     },
28815     
28816     onMouseUp : function()
28817     {
28818         this.hide();
28819     }
28820     
28821 });
28822
28823  
28824  /*
28825  * - LGPL
28826  *
28827  * menu item
28828  * 
28829  */
28830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28831
28832 /**
28833  * @class Roo.bootstrap.menu.Item
28834  * @extends Roo.bootstrap.Component
28835  * Bootstrap MenuItem class
28836  * @cfg {Boolean} submenu (true | false) default false
28837  * @cfg {String} html text of the item
28838  * @cfg {String} href the link
28839  * @cfg {Boolean} disable (true | false) default false
28840  * @cfg {Boolean} preventDefault (true | false) default true
28841  * @cfg {String} icon Font awesome icon
28842  * @cfg {String} pos Submenu align to (left | right) default right 
28843  * 
28844  * 
28845  * @constructor
28846  * Create a new Item
28847  * @param {Object} config The config object
28848  */
28849
28850
28851 Roo.bootstrap.menu.Item = function(config){
28852     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28853     this.addEvents({
28854         /**
28855          * @event mouseover
28856          * Fires when the mouse is hovering over this menu
28857          * @param {Roo.bootstrap.menu.Item} this
28858          * @param {Roo.EventObject} e
28859          */
28860         mouseover : true,
28861         /**
28862          * @event mouseout
28863          * Fires when the mouse exits this menu
28864          * @param {Roo.bootstrap.menu.Item} this
28865          * @param {Roo.EventObject} e
28866          */
28867         mouseout : true,
28868         // raw events
28869         /**
28870          * @event click
28871          * The raw click event for the entire grid.
28872          * @param {Roo.EventObject} e
28873          */
28874         click : true
28875     });
28876 };
28877
28878 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28879     
28880     submenu : false,
28881     href : '',
28882     html : '',
28883     preventDefault: true,
28884     disable : false,
28885     icon : false,
28886     pos : 'right',
28887     
28888     getAutoCreate : function()
28889     {
28890         var text = [
28891             {
28892                 tag : 'span',
28893                 cls : 'roo-menu-item-text',
28894                 html : this.html
28895             }
28896         ];
28897         
28898         if(this.icon){
28899             text.unshift({
28900                 tag : 'i',
28901                 cls : 'fa ' + this.icon
28902             })
28903         }
28904         
28905         var cfg = {
28906             tag : 'li',
28907             cn : [
28908                 {
28909                     tag : 'a',
28910                     href : this.href || '#',
28911                     cn : text
28912                 }
28913             ]
28914         };
28915         
28916         if(this.disable){
28917             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28918         }
28919         
28920         if(this.submenu){
28921             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28922             
28923             if(this.pos == 'left'){
28924                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28925             }
28926         }
28927         
28928         return cfg;
28929     },
28930     
28931     initEvents : function() 
28932     {
28933         this.el.on('mouseover', this.onMouseOver, this);
28934         this.el.on('mouseout', this.onMouseOut, this);
28935         
28936         this.el.select('a', true).first().on('click', this.onClick, this);
28937         
28938     },
28939     
28940     onClick : function(e)
28941     {
28942         if(this.preventDefault){
28943             e.preventDefault();
28944         }
28945         
28946         this.fireEvent("click", this, e);
28947     },
28948     
28949     onMouseOver : function(e)
28950     {
28951         if(this.submenu && this.pos == 'left'){
28952             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28953         }
28954         
28955         this.fireEvent("mouseover", this, e);
28956     },
28957     
28958     onMouseOut : function(e)
28959     {
28960         this.fireEvent("mouseout", this, e);
28961     }
28962 });
28963
28964  
28965
28966  /*
28967  * - LGPL
28968  *
28969  * menu separator
28970  * 
28971  */
28972 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28973
28974 /**
28975  * @class Roo.bootstrap.menu.Separator
28976  * @extends Roo.bootstrap.Component
28977  * Bootstrap Separator class
28978  * 
28979  * @constructor
28980  * Create a new Separator
28981  * @param {Object} config The config object
28982  */
28983
28984
28985 Roo.bootstrap.menu.Separator = function(config){
28986     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28987 };
28988
28989 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28990     
28991     getAutoCreate : function(){
28992         var cfg = {
28993             tag : 'li',
28994             cls: 'dropdown-divider divider'
28995         };
28996         
28997         return cfg;
28998     }
28999    
29000 });
29001
29002  
29003
29004  /*
29005  * - LGPL
29006  *
29007  * Tooltip
29008  * 
29009  */
29010
29011 /**
29012  * @class Roo.bootstrap.Tooltip
29013  * Bootstrap Tooltip class
29014  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29015  * to determine which dom element triggers the tooltip.
29016  * 
29017  * It needs to add support for additional attributes like tooltip-position
29018  * 
29019  * @constructor
29020  * Create a new Toolti
29021  * @param {Object} config The config object
29022  */
29023
29024 Roo.bootstrap.Tooltip = function(config){
29025     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29026     
29027     this.alignment = Roo.bootstrap.Tooltip.alignment;
29028     
29029     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29030         this.alignment = config.alignment;
29031     }
29032     
29033 };
29034
29035 Roo.apply(Roo.bootstrap.Tooltip, {
29036     /**
29037      * @function init initialize tooltip monitoring.
29038      * @static
29039      */
29040     currentEl : false,
29041     currentTip : false,
29042     currentRegion : false,
29043     
29044     //  init : delay?
29045     
29046     init : function()
29047     {
29048         Roo.get(document).on('mouseover', this.enter ,this);
29049         Roo.get(document).on('mouseout', this.leave, this);
29050          
29051         
29052         this.currentTip = new Roo.bootstrap.Tooltip();
29053     },
29054     
29055     enter : function(ev)
29056     {
29057         var dom = ev.getTarget();
29058         
29059         //Roo.log(['enter',dom]);
29060         var el = Roo.fly(dom);
29061         if (this.currentEl) {
29062             //Roo.log(dom);
29063             //Roo.log(this.currentEl);
29064             //Roo.log(this.currentEl.contains(dom));
29065             if (this.currentEl == el) {
29066                 return;
29067             }
29068             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29069                 return;
29070             }
29071
29072         }
29073         
29074         if (this.currentTip.el) {
29075             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29076         }    
29077         //Roo.log(ev);
29078         
29079         if(!el || el.dom == document){
29080             return;
29081         }
29082         
29083         var bindEl = el; 
29084         var pel = false;
29085         if (!el.attr('tooltip')) {
29086             pel = el.findParent("[tooltip]");
29087             if (pel) {
29088                 bindEl = Roo.get(pel);
29089             }
29090         }
29091         
29092        
29093         
29094         // you can not look for children, as if el is the body.. then everythign is the child..
29095         if (!pel && !el.attr('tooltip')) { //
29096             if (!el.select("[tooltip]").elements.length) {
29097                 return;
29098             }
29099             // is the mouse over this child...?
29100             bindEl = el.select("[tooltip]").first();
29101             var xy = ev.getXY();
29102             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29103                 //Roo.log("not in region.");
29104                 return;
29105             }
29106             //Roo.log("child element over..");
29107             
29108         }
29109         this.currentEl = el;
29110         this.currentTip.bind(bindEl);
29111         this.currentRegion = Roo.lib.Region.getRegion(dom);
29112         this.currentTip.enter();
29113         
29114     },
29115     leave : function(ev)
29116     {
29117         var dom = ev.getTarget();
29118         //Roo.log(['leave',dom]);
29119         if (!this.currentEl) {
29120             return;
29121         }
29122         
29123         
29124         if (dom != this.currentEl.dom) {
29125             return;
29126         }
29127         var xy = ev.getXY();
29128         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29129             return;
29130         }
29131         // only activate leave if mouse cursor is outside... bounding box..
29132         
29133         
29134         
29135         
29136         if (this.currentTip) {
29137             this.currentTip.leave();
29138         }
29139         //Roo.log('clear currentEl');
29140         this.currentEl = false;
29141         
29142         
29143     },
29144     alignment : {
29145         'left' : ['r-l', [-2,0], 'right'],
29146         'right' : ['l-r', [2,0], 'left'],
29147         'bottom' : ['t-b', [0,2], 'top'],
29148         'top' : [ 'b-t', [0,-2], 'bottom']
29149     }
29150     
29151 });
29152
29153
29154 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29155     
29156     
29157     bindEl : false,
29158     
29159     delay : null, // can be { show : 300 , hide: 500}
29160     
29161     timeout : null,
29162     
29163     hoverState : null, //???
29164     
29165     placement : 'bottom', 
29166     
29167     alignment : false,
29168     
29169     getAutoCreate : function(){
29170     
29171         var cfg = {
29172            cls : 'tooltip',   
29173            role : 'tooltip',
29174            cn : [
29175                 {
29176                     cls : 'tooltip-arrow arrow'
29177                 },
29178                 {
29179                     cls : 'tooltip-inner'
29180                 }
29181            ]
29182         };
29183         
29184         return cfg;
29185     },
29186     bind : function(el)
29187     {
29188         this.bindEl = el;
29189     },
29190     
29191     initEvents : function()
29192     {
29193         this.arrowEl = this.el.select('.arrow', true).first();
29194         this.innerEl = this.el.select('.tooltip-inner', true).first();
29195     },
29196     
29197     enter : function () {
29198        
29199         if (this.timeout != null) {
29200             clearTimeout(this.timeout);
29201         }
29202         
29203         this.hoverState = 'in';
29204          //Roo.log("enter - show");
29205         if (!this.delay || !this.delay.show) {
29206             this.show();
29207             return;
29208         }
29209         var _t = this;
29210         this.timeout = setTimeout(function () {
29211             if (_t.hoverState == 'in') {
29212                 _t.show();
29213             }
29214         }, this.delay.show);
29215     },
29216     leave : function()
29217     {
29218         clearTimeout(this.timeout);
29219     
29220         this.hoverState = 'out';
29221          if (!this.delay || !this.delay.hide) {
29222             this.hide();
29223             return;
29224         }
29225        
29226         var _t = this;
29227         this.timeout = setTimeout(function () {
29228             //Roo.log("leave - timeout");
29229             
29230             if (_t.hoverState == 'out') {
29231                 _t.hide();
29232                 Roo.bootstrap.Tooltip.currentEl = false;
29233             }
29234         }, delay);
29235     },
29236     
29237     show : function (msg)
29238     {
29239         if (!this.el) {
29240             this.render(document.body);
29241         }
29242         // set content.
29243         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29244         
29245         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29246         
29247         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29248         
29249         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29250                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29251         
29252         var placement = typeof this.placement == 'function' ?
29253             this.placement.call(this, this.el, on_el) :
29254             this.placement;
29255             
29256         var autoToken = /\s?auto?\s?/i;
29257         var autoPlace = autoToken.test(placement);
29258         if (autoPlace) {
29259             placement = placement.replace(autoToken, '') || 'top';
29260         }
29261         
29262         //this.el.detach()
29263         //this.el.setXY([0,0]);
29264         this.el.show();
29265         //this.el.dom.style.display='block';
29266         
29267         //this.el.appendTo(on_el);
29268         
29269         var p = this.getPosition();
29270         var box = this.el.getBox();
29271         
29272         if (autoPlace) {
29273             // fixme..
29274         }
29275         
29276         var align = this.alignment[placement];
29277         
29278         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29279         
29280         if(placement == 'top' || placement == 'bottom'){
29281             if(xy[0] < 0){
29282                 placement = 'right';
29283             }
29284             
29285             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29286                 placement = 'left';
29287             }
29288             
29289             var scroll = Roo.select('body', true).first().getScroll();
29290             
29291             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29292                 placement = 'top';
29293             }
29294             
29295             align = this.alignment[placement];
29296             
29297             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29298             
29299         }
29300         
29301         var elems = document.getElementsByTagName('div');
29302         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29303         for (var i = 0; i < elems.length; i++) {
29304           var zindex = Number.parseInt(
29305                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29306                 10
29307           );
29308           if (zindex > highest) {
29309             highest = zindex;
29310           }
29311         }
29312         
29313         
29314         
29315         this.el.dom.style.zIndex = highest;
29316         
29317         this.el.alignTo(this.bindEl, align[0],align[1]);
29318         //var arrow = this.el.select('.arrow',true).first();
29319         //arrow.set(align[2], 
29320         
29321         this.el.addClass(placement);
29322         this.el.addClass("bs-tooltip-"+ placement);
29323         
29324         this.el.addClass('in fade show');
29325         
29326         this.hoverState = null;
29327         
29328         if (this.el.hasClass('fade')) {
29329             // fade it?
29330         }
29331         
29332         
29333         
29334         
29335         
29336     },
29337     hide : function()
29338     {
29339          
29340         if (!this.el) {
29341             return;
29342         }
29343         //this.el.setXY([0,0]);
29344         this.el.removeClass(['show', 'in']);
29345         //this.el.hide();
29346         
29347     }
29348     
29349 });
29350  
29351
29352  /*
29353  * - LGPL
29354  *
29355  * Location Picker
29356  * 
29357  */
29358
29359 /**
29360  * @class Roo.bootstrap.LocationPicker
29361  * @extends Roo.bootstrap.Component
29362  * Bootstrap LocationPicker class
29363  * @cfg {Number} latitude Position when init default 0
29364  * @cfg {Number} longitude Position when init default 0
29365  * @cfg {Number} zoom default 15
29366  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29367  * @cfg {Boolean} mapTypeControl default false
29368  * @cfg {Boolean} disableDoubleClickZoom default false
29369  * @cfg {Boolean} scrollwheel default true
29370  * @cfg {Boolean} streetViewControl default false
29371  * @cfg {Number} radius default 0
29372  * @cfg {String} locationName
29373  * @cfg {Boolean} draggable default true
29374  * @cfg {Boolean} enableAutocomplete default false
29375  * @cfg {Boolean} enableReverseGeocode default true
29376  * @cfg {String} markerTitle
29377  * 
29378  * @constructor
29379  * Create a new LocationPicker
29380  * @param {Object} config The config object
29381  */
29382
29383
29384 Roo.bootstrap.LocationPicker = function(config){
29385     
29386     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29387     
29388     this.addEvents({
29389         /**
29390          * @event initial
29391          * Fires when the picker initialized.
29392          * @param {Roo.bootstrap.LocationPicker} this
29393          * @param {Google Location} location
29394          */
29395         initial : true,
29396         /**
29397          * @event positionchanged
29398          * Fires when the picker position changed.
29399          * @param {Roo.bootstrap.LocationPicker} this
29400          * @param {Google Location} location
29401          */
29402         positionchanged : true,
29403         /**
29404          * @event resize
29405          * Fires when the map resize.
29406          * @param {Roo.bootstrap.LocationPicker} this
29407          */
29408         resize : true,
29409         /**
29410          * @event show
29411          * Fires when the map show.
29412          * @param {Roo.bootstrap.LocationPicker} this
29413          */
29414         show : true,
29415         /**
29416          * @event hide
29417          * Fires when the map hide.
29418          * @param {Roo.bootstrap.LocationPicker} this
29419          */
29420         hide : true,
29421         /**
29422          * @event mapClick
29423          * Fires when click the map.
29424          * @param {Roo.bootstrap.LocationPicker} this
29425          * @param {Map event} e
29426          */
29427         mapClick : true,
29428         /**
29429          * @event mapRightClick
29430          * Fires when right click the map.
29431          * @param {Roo.bootstrap.LocationPicker} this
29432          * @param {Map event} e
29433          */
29434         mapRightClick : true,
29435         /**
29436          * @event markerClick
29437          * Fires when click the marker.
29438          * @param {Roo.bootstrap.LocationPicker} this
29439          * @param {Map event} e
29440          */
29441         markerClick : true,
29442         /**
29443          * @event markerRightClick
29444          * Fires when right click the marker.
29445          * @param {Roo.bootstrap.LocationPicker} this
29446          * @param {Map event} e
29447          */
29448         markerRightClick : true,
29449         /**
29450          * @event OverlayViewDraw
29451          * Fires when OverlayView Draw
29452          * @param {Roo.bootstrap.LocationPicker} this
29453          */
29454         OverlayViewDraw : true,
29455         /**
29456          * @event OverlayViewOnAdd
29457          * Fires when OverlayView Draw
29458          * @param {Roo.bootstrap.LocationPicker} this
29459          */
29460         OverlayViewOnAdd : true,
29461         /**
29462          * @event OverlayViewOnRemove
29463          * Fires when OverlayView Draw
29464          * @param {Roo.bootstrap.LocationPicker} this
29465          */
29466         OverlayViewOnRemove : true,
29467         /**
29468          * @event OverlayViewShow
29469          * Fires when OverlayView Draw
29470          * @param {Roo.bootstrap.LocationPicker} this
29471          * @param {Pixel} cpx
29472          */
29473         OverlayViewShow : true,
29474         /**
29475          * @event OverlayViewHide
29476          * Fires when OverlayView Draw
29477          * @param {Roo.bootstrap.LocationPicker} this
29478          */
29479         OverlayViewHide : true,
29480         /**
29481          * @event loadexception
29482          * Fires when load google lib failed.
29483          * @param {Roo.bootstrap.LocationPicker} this
29484          */
29485         loadexception : true
29486     });
29487         
29488 };
29489
29490 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29491     
29492     gMapContext: false,
29493     
29494     latitude: 0,
29495     longitude: 0,
29496     zoom: 15,
29497     mapTypeId: false,
29498     mapTypeControl: false,
29499     disableDoubleClickZoom: false,
29500     scrollwheel: true,
29501     streetViewControl: false,
29502     radius: 0,
29503     locationName: '',
29504     draggable: true,
29505     enableAutocomplete: false,
29506     enableReverseGeocode: true,
29507     markerTitle: '',
29508     
29509     getAutoCreate: function()
29510     {
29511
29512         var cfg = {
29513             tag: 'div',
29514             cls: 'roo-location-picker'
29515         };
29516         
29517         return cfg
29518     },
29519     
29520     initEvents: function(ct, position)
29521     {       
29522         if(!this.el.getWidth() || this.isApplied()){
29523             return;
29524         }
29525         
29526         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29527         
29528         this.initial();
29529     },
29530     
29531     initial: function()
29532     {
29533         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29534             this.fireEvent('loadexception', this);
29535             return;
29536         }
29537         
29538         if(!this.mapTypeId){
29539             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29540         }
29541         
29542         this.gMapContext = this.GMapContext();
29543         
29544         this.initOverlayView();
29545         
29546         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29547         
29548         var _this = this;
29549                 
29550         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29551             _this.setPosition(_this.gMapContext.marker.position);
29552         });
29553         
29554         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29555             _this.fireEvent('mapClick', this, event);
29556             
29557         });
29558
29559         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29560             _this.fireEvent('mapRightClick', this, event);
29561             
29562         });
29563         
29564         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29565             _this.fireEvent('markerClick', this, event);
29566             
29567         });
29568
29569         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29570             _this.fireEvent('markerRightClick', this, event);
29571             
29572         });
29573         
29574         this.setPosition(this.gMapContext.location);
29575         
29576         this.fireEvent('initial', this, this.gMapContext.location);
29577     },
29578     
29579     initOverlayView: function()
29580     {
29581         var _this = this;
29582         
29583         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29584             
29585             draw: function()
29586             {
29587                 _this.fireEvent('OverlayViewDraw', _this);
29588             },
29589             
29590             onAdd: function()
29591             {
29592                 _this.fireEvent('OverlayViewOnAdd', _this);
29593             },
29594             
29595             onRemove: function()
29596             {
29597                 _this.fireEvent('OverlayViewOnRemove', _this);
29598             },
29599             
29600             show: function(cpx)
29601             {
29602                 _this.fireEvent('OverlayViewShow', _this, cpx);
29603             },
29604             
29605             hide: function()
29606             {
29607                 _this.fireEvent('OverlayViewHide', _this);
29608             }
29609             
29610         });
29611     },
29612     
29613     fromLatLngToContainerPixel: function(event)
29614     {
29615         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29616     },
29617     
29618     isApplied: function() 
29619     {
29620         return this.getGmapContext() == false ? false : true;
29621     },
29622     
29623     getGmapContext: function() 
29624     {
29625         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29626     },
29627     
29628     GMapContext: function() 
29629     {
29630         var position = new google.maps.LatLng(this.latitude, this.longitude);
29631         
29632         var _map = new google.maps.Map(this.el.dom, {
29633             center: position,
29634             zoom: this.zoom,
29635             mapTypeId: this.mapTypeId,
29636             mapTypeControl: this.mapTypeControl,
29637             disableDoubleClickZoom: this.disableDoubleClickZoom,
29638             scrollwheel: this.scrollwheel,
29639             streetViewControl: this.streetViewControl,
29640             locationName: this.locationName,
29641             draggable: this.draggable,
29642             enableAutocomplete: this.enableAutocomplete,
29643             enableReverseGeocode: this.enableReverseGeocode
29644         });
29645         
29646         var _marker = new google.maps.Marker({
29647             position: position,
29648             map: _map,
29649             title: this.markerTitle,
29650             draggable: this.draggable
29651         });
29652         
29653         return {
29654             map: _map,
29655             marker: _marker,
29656             circle: null,
29657             location: position,
29658             radius: this.radius,
29659             locationName: this.locationName,
29660             addressComponents: {
29661                 formatted_address: null,
29662                 addressLine1: null,
29663                 addressLine2: null,
29664                 streetName: null,
29665                 streetNumber: null,
29666                 city: null,
29667                 district: null,
29668                 state: null,
29669                 stateOrProvince: null
29670             },
29671             settings: this,
29672             domContainer: this.el.dom,
29673             geodecoder: new google.maps.Geocoder()
29674         };
29675     },
29676     
29677     drawCircle: function(center, radius, options) 
29678     {
29679         if (this.gMapContext.circle != null) {
29680             this.gMapContext.circle.setMap(null);
29681         }
29682         if (radius > 0) {
29683             radius *= 1;
29684             options = Roo.apply({}, options, {
29685                 strokeColor: "#0000FF",
29686                 strokeOpacity: .35,
29687                 strokeWeight: 2,
29688                 fillColor: "#0000FF",
29689                 fillOpacity: .2
29690             });
29691             
29692             options.map = this.gMapContext.map;
29693             options.radius = radius;
29694             options.center = center;
29695             this.gMapContext.circle = new google.maps.Circle(options);
29696             return this.gMapContext.circle;
29697         }
29698         
29699         return null;
29700     },
29701     
29702     setPosition: function(location) 
29703     {
29704         this.gMapContext.location = location;
29705         this.gMapContext.marker.setPosition(location);
29706         this.gMapContext.map.panTo(location);
29707         this.drawCircle(location, this.gMapContext.radius, {});
29708         
29709         var _this = this;
29710         
29711         if (this.gMapContext.settings.enableReverseGeocode) {
29712             this.gMapContext.geodecoder.geocode({
29713                 latLng: this.gMapContext.location
29714             }, function(results, status) {
29715                 
29716                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29717                     _this.gMapContext.locationName = results[0].formatted_address;
29718                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29719                     
29720                     _this.fireEvent('positionchanged', this, location);
29721                 }
29722             });
29723             
29724             return;
29725         }
29726         
29727         this.fireEvent('positionchanged', this, location);
29728     },
29729     
29730     resize: function()
29731     {
29732         google.maps.event.trigger(this.gMapContext.map, "resize");
29733         
29734         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29735         
29736         this.fireEvent('resize', this);
29737     },
29738     
29739     setPositionByLatLng: function(latitude, longitude)
29740     {
29741         this.setPosition(new google.maps.LatLng(latitude, longitude));
29742     },
29743     
29744     getCurrentPosition: function() 
29745     {
29746         return {
29747             latitude: this.gMapContext.location.lat(),
29748             longitude: this.gMapContext.location.lng()
29749         };
29750     },
29751     
29752     getAddressName: function() 
29753     {
29754         return this.gMapContext.locationName;
29755     },
29756     
29757     getAddressComponents: function() 
29758     {
29759         return this.gMapContext.addressComponents;
29760     },
29761     
29762     address_component_from_google_geocode: function(address_components) 
29763     {
29764         var result = {};
29765         
29766         for (var i = 0; i < address_components.length; i++) {
29767             var component = address_components[i];
29768             if (component.types.indexOf("postal_code") >= 0) {
29769                 result.postalCode = component.short_name;
29770             } else if (component.types.indexOf("street_number") >= 0) {
29771                 result.streetNumber = component.short_name;
29772             } else if (component.types.indexOf("route") >= 0) {
29773                 result.streetName = component.short_name;
29774             } else if (component.types.indexOf("neighborhood") >= 0) {
29775                 result.city = component.short_name;
29776             } else if (component.types.indexOf("locality") >= 0) {
29777                 result.city = component.short_name;
29778             } else if (component.types.indexOf("sublocality") >= 0) {
29779                 result.district = component.short_name;
29780             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29781                 result.stateOrProvince = component.short_name;
29782             } else if (component.types.indexOf("country") >= 0) {
29783                 result.country = component.short_name;
29784             }
29785         }
29786         
29787         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29788         result.addressLine2 = "";
29789         return result;
29790     },
29791     
29792     setZoomLevel: function(zoom)
29793     {
29794         this.gMapContext.map.setZoom(zoom);
29795     },
29796     
29797     show: function()
29798     {
29799         if(!this.el){
29800             return;
29801         }
29802         
29803         this.el.show();
29804         
29805         this.resize();
29806         
29807         this.fireEvent('show', this);
29808     },
29809     
29810     hide: function()
29811     {
29812         if(!this.el){
29813             return;
29814         }
29815         
29816         this.el.hide();
29817         
29818         this.fireEvent('hide', this);
29819     }
29820     
29821 });
29822
29823 Roo.apply(Roo.bootstrap.LocationPicker, {
29824     
29825     OverlayView : function(map, options)
29826     {
29827         options = options || {};
29828         
29829         this.setMap(map);
29830     }
29831     
29832     
29833 });/**
29834  * @class Roo.bootstrap.Alert
29835  * @extends Roo.bootstrap.Component
29836  * Bootstrap Alert class - shows an alert area box
29837  * eg
29838  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29839   Enter a valid email address
29840 </div>
29841  * @licence LGPL
29842  * @cfg {String} title The title of alert
29843  * @cfg {String} html The content of alert
29844  * @cfg {String} weight (  success | info | warning | danger )
29845  * @cfg {String} fa font-awesomeicon
29846  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29847  * @cfg {Boolean} close true to show a x closer
29848  * 
29849  * 
29850  * @constructor
29851  * Create a new alert
29852  * @param {Object} config The config object
29853  */
29854
29855
29856 Roo.bootstrap.Alert = function(config){
29857     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29858     
29859 };
29860
29861 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29862     
29863     title: '',
29864     html: '',
29865     weight: false,
29866     fa: false,
29867     faicon: false, // BC
29868     close : false,
29869     
29870     
29871     getAutoCreate : function()
29872     {
29873         
29874         var cfg = {
29875             tag : 'div',
29876             cls : 'alert',
29877             cn : [
29878                 {
29879                     tag: 'button',
29880                     type :  "button",
29881                     cls: "close",
29882                     html : '×',
29883                     style : this.close ? '' : 'display:none'
29884                 },
29885                 {
29886                     tag : 'i',
29887                     cls : 'roo-alert-icon'
29888                     
29889                 },
29890                 {
29891                     tag : 'b',
29892                     cls : 'roo-alert-title',
29893                     html : this.title
29894                 },
29895                 {
29896                     tag : 'span',
29897                     cls : 'roo-alert-text',
29898                     html : this.html
29899                 }
29900             ]
29901         };
29902         
29903         if(this.faicon){
29904             cfg.cn[0].cls += ' fa ' + this.faicon;
29905         }
29906         if(this.fa){
29907             cfg.cn[0].cls += ' fa ' + this.fa;
29908         }
29909         
29910         if(this.weight){
29911             cfg.cls += ' alert-' + this.weight;
29912         }
29913         
29914         return cfg;
29915     },
29916     
29917     initEvents: function() 
29918     {
29919         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29920         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29921         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29922         if (this.seconds > 0) {
29923             this.hide.defer(this.seconds, this);
29924         }
29925     },
29926     
29927     setTitle : function(str)
29928     {
29929         this.titleEl.dom.innerHTML = str;
29930     },
29931     
29932     setText : function(str)
29933     {
29934         this.titleEl.dom.innerHTML = str;
29935     },
29936     
29937     setWeight : function(weight)
29938     {
29939         if(this.weight){
29940             this.el.removeClass('alert-' + this.weight);
29941         }
29942         
29943         this.weight = weight;
29944         
29945         this.el.addClass('alert-' + this.weight);
29946     },
29947     
29948     setIcon : function(icon)
29949     {
29950         if(this.faicon){
29951             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29952         }
29953         
29954         this.faicon = icon;
29955         
29956         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29957     },
29958     
29959     hide: function() 
29960     {
29961         this.el.hide();   
29962     },
29963     
29964     show: function() 
29965     {  
29966         this.el.show();   
29967     }
29968     
29969 });
29970
29971  
29972 /*
29973 * Licence: LGPL
29974 */
29975
29976 /**
29977  * @class Roo.bootstrap.UploadCropbox
29978  * @extends Roo.bootstrap.Component
29979  * Bootstrap UploadCropbox class
29980  * @cfg {String} emptyText show when image has been loaded
29981  * @cfg {String} rotateNotify show when image too small to rotate
29982  * @cfg {Number} errorTimeout default 3000
29983  * @cfg {Number} minWidth default 300
29984  * @cfg {Number} minHeight default 300
29985  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29986  * @cfg {Boolean} isDocument (true|false) default false
29987  * @cfg {String} url action url
29988  * @cfg {String} paramName default 'imageUpload'
29989  * @cfg {String} method default POST
29990  * @cfg {Boolean} loadMask (true|false) default true
29991  * @cfg {Boolean} loadingText default 'Loading...'
29992  * 
29993  * @constructor
29994  * Create a new UploadCropbox
29995  * @param {Object} config The config object
29996  */
29997
29998 Roo.bootstrap.UploadCropbox = function(config){
29999     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30000     
30001     this.addEvents({
30002         /**
30003          * @event beforeselectfile
30004          * Fire before select file
30005          * @param {Roo.bootstrap.UploadCropbox} this
30006          */
30007         "beforeselectfile" : true,
30008         /**
30009          * @event initial
30010          * Fire after initEvent
30011          * @param {Roo.bootstrap.UploadCropbox} this
30012          */
30013         "initial" : true,
30014         /**
30015          * @event crop
30016          * Fire after initEvent
30017          * @param {Roo.bootstrap.UploadCropbox} this
30018          * @param {String} data
30019          */
30020         "crop" : true,
30021         /**
30022          * @event prepare
30023          * Fire when preparing the file data
30024          * @param {Roo.bootstrap.UploadCropbox} this
30025          * @param {Object} file
30026          */
30027         "prepare" : true,
30028         /**
30029          * @event exception
30030          * Fire when get exception
30031          * @param {Roo.bootstrap.UploadCropbox} this
30032          * @param {XMLHttpRequest} xhr
30033          */
30034         "exception" : true,
30035         /**
30036          * @event beforeloadcanvas
30037          * Fire before load the canvas
30038          * @param {Roo.bootstrap.UploadCropbox} this
30039          * @param {String} src
30040          */
30041         "beforeloadcanvas" : true,
30042         /**
30043          * @event trash
30044          * Fire when trash image
30045          * @param {Roo.bootstrap.UploadCropbox} this
30046          */
30047         "trash" : true,
30048         /**
30049          * @event download
30050          * Fire when download the image
30051          * @param {Roo.bootstrap.UploadCropbox} this
30052          */
30053         "download" : true,
30054         /**
30055          * @event footerbuttonclick
30056          * Fire when footerbuttonclick
30057          * @param {Roo.bootstrap.UploadCropbox} this
30058          * @param {String} type
30059          */
30060         "footerbuttonclick" : true,
30061         /**
30062          * @event resize
30063          * Fire when resize
30064          * @param {Roo.bootstrap.UploadCropbox} this
30065          */
30066         "resize" : true,
30067         /**
30068          * @event rotate
30069          * Fire when rotate the image
30070          * @param {Roo.bootstrap.UploadCropbox} this
30071          * @param {String} pos
30072          */
30073         "rotate" : true,
30074         /**
30075          * @event inspect
30076          * Fire when inspect the file
30077          * @param {Roo.bootstrap.UploadCropbox} this
30078          * @param {Object} file
30079          */
30080         "inspect" : true,
30081         /**
30082          * @event upload
30083          * Fire when xhr upload the file
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          * @param {Object} data
30086          */
30087         "upload" : true,
30088         /**
30089          * @event arrange
30090          * Fire when arrange the file data
30091          * @param {Roo.bootstrap.UploadCropbox} this
30092          * @param {Object} formData
30093          */
30094         "arrange" : true
30095     });
30096     
30097     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30098 };
30099
30100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30101     
30102     emptyText : 'Click to upload image',
30103     rotateNotify : 'Image is too small to rotate',
30104     errorTimeout : 3000,
30105     scale : 0,
30106     baseScale : 1,
30107     rotate : 0,
30108     dragable : false,
30109     pinching : false,
30110     mouseX : 0,
30111     mouseY : 0,
30112     cropData : false,
30113     minWidth : 300,
30114     minHeight : 300,
30115     file : false,
30116     exif : {},
30117     baseRotate : 1,
30118     cropType : 'image/jpeg',
30119     buttons : false,
30120     canvasLoaded : false,
30121     isDocument : false,
30122     method : 'POST',
30123     paramName : 'imageUpload',
30124     loadMask : true,
30125     loadingText : 'Loading...',
30126     maskEl : false,
30127     
30128     getAutoCreate : function()
30129     {
30130         var cfg = {
30131             tag : 'div',
30132             cls : 'roo-upload-cropbox',
30133             cn : [
30134                 {
30135                     tag : 'input',
30136                     cls : 'roo-upload-cropbox-selector',
30137                     type : 'file'
30138                 },
30139                 {
30140                     tag : 'div',
30141                     cls : 'roo-upload-cropbox-body',
30142                     style : 'cursor:pointer',
30143                     cn : [
30144                         {
30145                             tag : 'div',
30146                             cls : 'roo-upload-cropbox-preview'
30147                         },
30148                         {
30149                             tag : 'div',
30150                             cls : 'roo-upload-cropbox-thumb'
30151                         },
30152                         {
30153                             tag : 'div',
30154                             cls : 'roo-upload-cropbox-empty-notify',
30155                             html : this.emptyText
30156                         },
30157                         {
30158                             tag : 'div',
30159                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30160                             html : this.rotateNotify
30161                         }
30162                     ]
30163                 },
30164                 {
30165                     tag : 'div',
30166                     cls : 'roo-upload-cropbox-footer',
30167                     cn : {
30168                         tag : 'div',
30169                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30170                         cn : []
30171                     }
30172                 }
30173             ]
30174         };
30175         
30176         return cfg;
30177     },
30178     
30179     onRender : function(ct, position)
30180     {
30181         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30182         
30183         if (this.buttons.length) {
30184             
30185             Roo.each(this.buttons, function(bb) {
30186                 
30187                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30188                 
30189                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30190                 
30191             }, this);
30192         }
30193         
30194         if(this.loadMask){
30195             this.maskEl = this.el;
30196         }
30197     },
30198     
30199     initEvents : function()
30200     {
30201         this.urlAPI = (window.createObjectURL && window) || 
30202                                 (window.URL && URL.revokeObjectURL && URL) || 
30203                                 (window.webkitURL && webkitURL);
30204                         
30205         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30206         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30207         
30208         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30209         this.selectorEl.hide();
30210         
30211         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30212         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30213         
30214         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30215         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30216         this.thumbEl.hide();
30217         
30218         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30219         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30220         
30221         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30222         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30223         this.errorEl.hide();
30224         
30225         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30226         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30227         this.footerEl.hide();
30228         
30229         this.setThumbBoxSize();
30230         
30231         this.bind();
30232         
30233         this.resize();
30234         
30235         this.fireEvent('initial', this);
30236     },
30237
30238     bind : function()
30239     {
30240         var _this = this;
30241         
30242         window.addEventListener("resize", function() { _this.resize(); } );
30243         
30244         this.bodyEl.on('click', this.beforeSelectFile, this);
30245         
30246         if(Roo.isTouch){
30247             this.bodyEl.on('touchstart', this.onTouchStart, this);
30248             this.bodyEl.on('touchmove', this.onTouchMove, this);
30249             this.bodyEl.on('touchend', this.onTouchEnd, this);
30250         }
30251         
30252         if(!Roo.isTouch){
30253             this.bodyEl.on('mousedown', this.onMouseDown, this);
30254             this.bodyEl.on('mousemove', this.onMouseMove, this);
30255             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30256             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30257             Roo.get(document).on('mouseup', this.onMouseUp, this);
30258         }
30259         
30260         this.selectorEl.on('change', this.onFileSelected, this);
30261     },
30262     
30263     reset : function()
30264     {    
30265         this.scale = 0;
30266         this.baseScale = 1;
30267         this.rotate = 0;
30268         this.baseRotate = 1;
30269         this.dragable = false;
30270         this.pinching = false;
30271         this.mouseX = 0;
30272         this.mouseY = 0;
30273         this.cropData = false;
30274         this.notifyEl.dom.innerHTML = this.emptyText;
30275         
30276         this.selectorEl.dom.value = '';
30277         
30278     },
30279     
30280     resize : function()
30281     {
30282         if(this.fireEvent('resize', this) != false){
30283             this.setThumbBoxPosition();
30284             this.setCanvasPosition();
30285         }
30286     },
30287     
30288     onFooterButtonClick : function(e, el, o, type)
30289     {
30290         switch (type) {
30291             case 'rotate-left' :
30292                 this.onRotateLeft(e);
30293                 break;
30294             case 'rotate-right' :
30295                 this.onRotateRight(e);
30296                 break;
30297             case 'picture' :
30298                 this.beforeSelectFile(e);
30299                 break;
30300             case 'trash' :
30301                 this.trash(e);
30302                 break;
30303             case 'crop' :
30304                 this.crop(e);
30305                 break;
30306             case 'download' :
30307                 this.download(e);
30308                 break;
30309             default :
30310                 break;
30311         }
30312         
30313         this.fireEvent('footerbuttonclick', this, type);
30314     },
30315     
30316     beforeSelectFile : function(e)
30317     {
30318         e.preventDefault();
30319         
30320         if(this.fireEvent('beforeselectfile', this) != false){
30321             this.selectorEl.dom.click();
30322         }
30323     },
30324     
30325     onFileSelected : function(e)
30326     {
30327         e.preventDefault();
30328         
30329         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30330             return;
30331         }
30332         
30333         var file = this.selectorEl.dom.files[0];
30334         
30335         if(this.fireEvent('inspect', this, file) != false){
30336             this.prepare(file);
30337         }
30338         
30339     },
30340     
30341     trash : function(e)
30342     {
30343         this.fireEvent('trash', this);
30344     },
30345     
30346     download : function(e)
30347     {
30348         this.fireEvent('download', this);
30349     },
30350     
30351     loadCanvas : function(src)
30352     {   
30353         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30354             
30355             this.reset();
30356             
30357             this.imageEl = document.createElement('img');
30358             
30359             var _this = this;
30360             
30361             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30362             
30363             this.imageEl.src = src;
30364         }
30365     },
30366     
30367     onLoadCanvas : function()
30368     {   
30369         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30370         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30371         
30372         this.bodyEl.un('click', this.beforeSelectFile, this);
30373         
30374         this.notifyEl.hide();
30375         this.thumbEl.show();
30376         this.footerEl.show();
30377         
30378         this.baseRotateLevel();
30379         
30380         if(this.isDocument){
30381             this.setThumbBoxSize();
30382         }
30383         
30384         this.setThumbBoxPosition();
30385         
30386         this.baseScaleLevel();
30387         
30388         this.draw();
30389         
30390         this.resize();
30391         
30392         this.canvasLoaded = true;
30393         
30394         if(this.loadMask){
30395             this.maskEl.unmask();
30396         }
30397         
30398     },
30399     
30400     setCanvasPosition : function()
30401     {   
30402         if(!this.canvasEl){
30403             return;
30404         }
30405         
30406         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30407         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30408         
30409         this.previewEl.setLeft(pw);
30410         this.previewEl.setTop(ph);
30411         
30412     },
30413     
30414     onMouseDown : function(e)
30415     {   
30416         e.stopEvent();
30417         
30418         this.dragable = true;
30419         this.pinching = false;
30420         
30421         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30422             this.dragable = false;
30423             return;
30424         }
30425         
30426         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30427         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30428         
30429     },
30430     
30431     onMouseMove : function(e)
30432     {   
30433         e.stopEvent();
30434         
30435         if(!this.canvasLoaded){
30436             return;
30437         }
30438         
30439         if (!this.dragable){
30440             return;
30441         }
30442         
30443         var minX = Math.ceil(this.thumbEl.getLeft(true));
30444         var minY = Math.ceil(this.thumbEl.getTop(true));
30445         
30446         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30447         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30448         
30449         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30450         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30451         
30452         x = x - this.mouseX;
30453         y = y - this.mouseY;
30454         
30455         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30456         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30457         
30458         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30459         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30460         
30461         this.previewEl.setLeft(bgX);
30462         this.previewEl.setTop(bgY);
30463         
30464         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30465         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30466     },
30467     
30468     onMouseUp : function(e)
30469     {   
30470         e.stopEvent();
30471         
30472         this.dragable = false;
30473     },
30474     
30475     onMouseWheel : function(e)
30476     {   
30477         e.stopEvent();
30478         
30479         this.startScale = this.scale;
30480         
30481         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30482         
30483         if(!this.zoomable()){
30484             this.scale = this.startScale;
30485             return;
30486         }
30487         
30488         this.draw();
30489         
30490         return;
30491     },
30492     
30493     zoomable : function()
30494     {
30495         var minScale = this.thumbEl.getWidth() / this.minWidth;
30496         
30497         if(this.minWidth < this.minHeight){
30498             minScale = this.thumbEl.getHeight() / this.minHeight;
30499         }
30500         
30501         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30502         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30503         
30504         if(
30505                 this.isDocument &&
30506                 (this.rotate == 0 || this.rotate == 180) && 
30507                 (
30508                     width > this.imageEl.OriginWidth || 
30509                     height > this.imageEl.OriginHeight ||
30510                     (width < this.minWidth && height < this.minHeight)
30511                 )
30512         ){
30513             return false;
30514         }
30515         
30516         if(
30517                 this.isDocument &&
30518                 (this.rotate == 90 || this.rotate == 270) && 
30519                 (
30520                     width > this.imageEl.OriginWidth || 
30521                     height > this.imageEl.OriginHeight ||
30522                     (width < this.minHeight && height < this.minWidth)
30523                 )
30524         ){
30525             return false;
30526         }
30527         
30528         if(
30529                 !this.isDocument &&
30530                 (this.rotate == 0 || this.rotate == 180) && 
30531                 (
30532                     width < this.minWidth || 
30533                     width > this.imageEl.OriginWidth || 
30534                     height < this.minHeight || 
30535                     height > this.imageEl.OriginHeight
30536                 )
30537         ){
30538             return false;
30539         }
30540         
30541         if(
30542                 !this.isDocument &&
30543                 (this.rotate == 90 || this.rotate == 270) && 
30544                 (
30545                     width < this.minHeight || 
30546                     width > this.imageEl.OriginWidth || 
30547                     height < this.minWidth || 
30548                     height > this.imageEl.OriginHeight
30549                 )
30550         ){
30551             return false;
30552         }
30553         
30554         return true;
30555         
30556     },
30557     
30558     onRotateLeft : function(e)
30559     {   
30560         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30561             
30562             var minScale = this.thumbEl.getWidth() / this.minWidth;
30563             
30564             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30565             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30566             
30567             this.startScale = this.scale;
30568             
30569             while (this.getScaleLevel() < minScale){
30570             
30571                 this.scale = this.scale + 1;
30572                 
30573                 if(!this.zoomable()){
30574                     break;
30575                 }
30576                 
30577                 if(
30578                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30579                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30580                 ){
30581                     continue;
30582                 }
30583                 
30584                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30585
30586                 this.draw();
30587                 
30588                 return;
30589             }
30590             
30591             this.scale = this.startScale;
30592             
30593             this.onRotateFail();
30594             
30595             return false;
30596         }
30597         
30598         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30599
30600         if(this.isDocument){
30601             this.setThumbBoxSize();
30602             this.setThumbBoxPosition();
30603             this.setCanvasPosition();
30604         }
30605         
30606         this.draw();
30607         
30608         this.fireEvent('rotate', this, 'left');
30609         
30610     },
30611     
30612     onRotateRight : function(e)
30613     {
30614         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30615             
30616             var minScale = this.thumbEl.getWidth() / this.minWidth;
30617         
30618             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30619             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30620             
30621             this.startScale = this.scale;
30622             
30623             while (this.getScaleLevel() < minScale){
30624             
30625                 this.scale = this.scale + 1;
30626                 
30627                 if(!this.zoomable()){
30628                     break;
30629                 }
30630                 
30631                 if(
30632                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30633                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30634                 ){
30635                     continue;
30636                 }
30637                 
30638                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30639
30640                 this.draw();
30641                 
30642                 return;
30643             }
30644             
30645             this.scale = this.startScale;
30646             
30647             this.onRotateFail();
30648             
30649             return false;
30650         }
30651         
30652         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30653
30654         if(this.isDocument){
30655             this.setThumbBoxSize();
30656             this.setThumbBoxPosition();
30657             this.setCanvasPosition();
30658         }
30659         
30660         this.draw();
30661         
30662         this.fireEvent('rotate', this, 'right');
30663     },
30664     
30665     onRotateFail : function()
30666     {
30667         this.errorEl.show(true);
30668         
30669         var _this = this;
30670         
30671         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30672     },
30673     
30674     draw : function()
30675     {
30676         this.previewEl.dom.innerHTML = '';
30677         
30678         var canvasEl = document.createElement("canvas");
30679         
30680         var contextEl = canvasEl.getContext("2d");
30681         
30682         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30683         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30684         var center = this.imageEl.OriginWidth / 2;
30685         
30686         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30687             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30688             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30689             center = this.imageEl.OriginHeight / 2;
30690         }
30691         
30692         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30693         
30694         contextEl.translate(center, center);
30695         contextEl.rotate(this.rotate * Math.PI / 180);
30696
30697         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30698         
30699         this.canvasEl = document.createElement("canvas");
30700         
30701         this.contextEl = this.canvasEl.getContext("2d");
30702         
30703         switch (this.rotate) {
30704             case 0 :
30705                 
30706                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30707                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30708                 
30709                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30710                 
30711                 break;
30712             case 90 : 
30713                 
30714                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30715                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30716                 
30717                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30718                     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);
30719                     break;
30720                 }
30721                 
30722                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30723                 
30724                 break;
30725             case 180 :
30726                 
30727                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30728                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30729                 
30730                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30731                     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);
30732                     break;
30733                 }
30734                 
30735                 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);
30736                 
30737                 break;
30738             case 270 :
30739                 
30740                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30741                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30742         
30743                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30744                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30745                     break;
30746                 }
30747                 
30748                 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);
30749                 
30750                 break;
30751             default : 
30752                 break;
30753         }
30754         
30755         this.previewEl.appendChild(this.canvasEl);
30756         
30757         this.setCanvasPosition();
30758     },
30759     
30760     crop : function()
30761     {
30762         if(!this.canvasLoaded){
30763             return;
30764         }
30765         
30766         var imageCanvas = document.createElement("canvas");
30767         
30768         var imageContext = imageCanvas.getContext("2d");
30769         
30770         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30771         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30772         
30773         var center = imageCanvas.width / 2;
30774         
30775         imageContext.translate(center, center);
30776         
30777         imageContext.rotate(this.rotate * Math.PI / 180);
30778         
30779         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30780         
30781         var canvas = document.createElement("canvas");
30782         
30783         var context = canvas.getContext("2d");
30784                 
30785         canvas.width = this.minWidth;
30786         canvas.height = this.minHeight;
30787
30788         switch (this.rotate) {
30789             case 0 :
30790                 
30791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30793                 
30794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30796                 
30797                 var targetWidth = this.minWidth - 2 * x;
30798                 var targetHeight = this.minHeight - 2 * y;
30799                 
30800                 var scale = 1;
30801                 
30802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30803                     scale = targetWidth / width;
30804                 }
30805                 
30806                 if(x > 0 && y == 0){
30807                     scale = targetHeight / height;
30808                 }
30809                 
30810                 if(x > 0 && y > 0){
30811                     scale = targetWidth / width;
30812                     
30813                     if(width < height){
30814                         scale = targetHeight / height;
30815                     }
30816                 }
30817                 
30818                 context.scale(scale, scale);
30819                 
30820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30822
30823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30825
30826                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30827                 
30828                 break;
30829             case 90 : 
30830                 
30831                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30832                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30833                 
30834                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30835                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30836                 
30837                 var targetWidth = this.minWidth - 2 * x;
30838                 var targetHeight = this.minHeight - 2 * y;
30839                 
30840                 var scale = 1;
30841                 
30842                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30843                     scale = targetWidth / width;
30844                 }
30845                 
30846                 if(x > 0 && y == 0){
30847                     scale = targetHeight / height;
30848                 }
30849                 
30850                 if(x > 0 && y > 0){
30851                     scale = targetWidth / width;
30852                     
30853                     if(width < height){
30854                         scale = targetHeight / height;
30855                     }
30856                 }
30857                 
30858                 context.scale(scale, scale);
30859                 
30860                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30861                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30862
30863                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30864                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30865                 
30866                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30867                 
30868                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30869                 
30870                 break;
30871             case 180 :
30872                 
30873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30875                 
30876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30878                 
30879                 var targetWidth = this.minWidth - 2 * x;
30880                 var targetHeight = this.minHeight - 2 * y;
30881                 
30882                 var scale = 1;
30883                 
30884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30885                     scale = targetWidth / width;
30886                 }
30887                 
30888                 if(x > 0 && y == 0){
30889                     scale = targetHeight / height;
30890                 }
30891                 
30892                 if(x > 0 && y > 0){
30893                     scale = targetWidth / width;
30894                     
30895                     if(width < height){
30896                         scale = targetHeight / height;
30897                     }
30898                 }
30899                 
30900                 context.scale(scale, scale);
30901                 
30902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30904
30905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30907
30908                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30909                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30910                 
30911                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30912                 
30913                 break;
30914             case 270 :
30915                 
30916                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30917                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30918                 
30919                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30920                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30921                 
30922                 var targetWidth = this.minWidth - 2 * x;
30923                 var targetHeight = this.minHeight - 2 * y;
30924                 
30925                 var scale = 1;
30926                 
30927                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30928                     scale = targetWidth / width;
30929                 }
30930                 
30931                 if(x > 0 && y == 0){
30932                     scale = targetHeight / height;
30933                 }
30934                 
30935                 if(x > 0 && y > 0){
30936                     scale = targetWidth / width;
30937                     
30938                     if(width < height){
30939                         scale = targetHeight / height;
30940                     }
30941                 }
30942                 
30943                 context.scale(scale, scale);
30944                 
30945                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30946                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30947
30948                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30949                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30950                 
30951                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30952                 
30953                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30954                 
30955                 break;
30956             default : 
30957                 break;
30958         }
30959         
30960         this.cropData = canvas.toDataURL(this.cropType);
30961         
30962         if(this.fireEvent('crop', this, this.cropData) !== false){
30963             this.process(this.file, this.cropData);
30964         }
30965         
30966         return;
30967         
30968     },
30969     
30970     setThumbBoxSize : function()
30971     {
30972         var width, height;
30973         
30974         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30975             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30976             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30977             
30978             this.minWidth = width;
30979             this.minHeight = height;
30980             
30981             if(this.rotate == 90 || this.rotate == 270){
30982                 this.minWidth = height;
30983                 this.minHeight = width;
30984             }
30985         }
30986         
30987         height = 300;
30988         width = Math.ceil(this.minWidth * height / this.minHeight);
30989         
30990         if(this.minWidth > this.minHeight){
30991             width = 300;
30992             height = Math.ceil(this.minHeight * width / this.minWidth);
30993         }
30994         
30995         this.thumbEl.setStyle({
30996             width : width + 'px',
30997             height : height + 'px'
30998         });
30999
31000         return;
31001             
31002     },
31003     
31004     setThumbBoxPosition : function()
31005     {
31006         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31007         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31008         
31009         this.thumbEl.setLeft(x);
31010         this.thumbEl.setTop(y);
31011         
31012     },
31013     
31014     baseRotateLevel : function()
31015     {
31016         this.baseRotate = 1;
31017         
31018         if(
31019                 typeof(this.exif) != 'undefined' &&
31020                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31021                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31022         ){
31023             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31024         }
31025         
31026         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31027         
31028     },
31029     
31030     baseScaleLevel : function()
31031     {
31032         var width, height;
31033         
31034         if(this.isDocument){
31035             
31036             if(this.baseRotate == 6 || this.baseRotate == 8){
31037             
31038                 height = this.thumbEl.getHeight();
31039                 this.baseScale = height / this.imageEl.OriginWidth;
31040
31041                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31042                     width = this.thumbEl.getWidth();
31043                     this.baseScale = width / this.imageEl.OriginHeight;
31044                 }
31045
31046                 return;
31047             }
31048
31049             height = this.thumbEl.getHeight();
31050             this.baseScale = height / this.imageEl.OriginHeight;
31051
31052             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31053                 width = this.thumbEl.getWidth();
31054                 this.baseScale = width / this.imageEl.OriginWidth;
31055             }
31056
31057             return;
31058         }
31059         
31060         if(this.baseRotate == 6 || this.baseRotate == 8){
31061             
31062             width = this.thumbEl.getHeight();
31063             this.baseScale = width / this.imageEl.OriginHeight;
31064             
31065             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31066                 height = this.thumbEl.getWidth();
31067                 this.baseScale = height / this.imageEl.OriginHeight;
31068             }
31069             
31070             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31071                 height = this.thumbEl.getWidth();
31072                 this.baseScale = height / this.imageEl.OriginHeight;
31073                 
31074                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31075                     width = this.thumbEl.getHeight();
31076                     this.baseScale = width / this.imageEl.OriginWidth;
31077                 }
31078             }
31079             
31080             return;
31081         }
31082         
31083         width = this.thumbEl.getWidth();
31084         this.baseScale = width / this.imageEl.OriginWidth;
31085         
31086         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31087             height = this.thumbEl.getHeight();
31088             this.baseScale = height / this.imageEl.OriginHeight;
31089         }
31090         
31091         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31092             
31093             height = this.thumbEl.getHeight();
31094             this.baseScale = height / this.imageEl.OriginHeight;
31095             
31096             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31097                 width = this.thumbEl.getWidth();
31098                 this.baseScale = width / this.imageEl.OriginWidth;
31099             }
31100             
31101         }
31102         
31103         return;
31104     },
31105     
31106     getScaleLevel : function()
31107     {
31108         return this.baseScale * Math.pow(1.1, this.scale);
31109     },
31110     
31111     onTouchStart : function(e)
31112     {
31113         if(!this.canvasLoaded){
31114             this.beforeSelectFile(e);
31115             return;
31116         }
31117         
31118         var touches = e.browserEvent.touches;
31119         
31120         if(!touches){
31121             return;
31122         }
31123         
31124         if(touches.length == 1){
31125             this.onMouseDown(e);
31126             return;
31127         }
31128         
31129         if(touches.length != 2){
31130             return;
31131         }
31132         
31133         var coords = [];
31134         
31135         for(var i = 0, finger; finger = touches[i]; i++){
31136             coords.push(finger.pageX, finger.pageY);
31137         }
31138         
31139         var x = Math.pow(coords[0] - coords[2], 2);
31140         var y = Math.pow(coords[1] - coords[3], 2);
31141         
31142         this.startDistance = Math.sqrt(x + y);
31143         
31144         this.startScale = this.scale;
31145         
31146         this.pinching = true;
31147         this.dragable = false;
31148         
31149     },
31150     
31151     onTouchMove : function(e)
31152     {
31153         if(!this.pinching && !this.dragable){
31154             return;
31155         }
31156         
31157         var touches = e.browserEvent.touches;
31158         
31159         if(!touches){
31160             return;
31161         }
31162         
31163         if(this.dragable){
31164             this.onMouseMove(e);
31165             return;
31166         }
31167         
31168         var coords = [];
31169         
31170         for(var i = 0, finger; finger = touches[i]; i++){
31171             coords.push(finger.pageX, finger.pageY);
31172         }
31173         
31174         var x = Math.pow(coords[0] - coords[2], 2);
31175         var y = Math.pow(coords[1] - coords[3], 2);
31176         
31177         this.endDistance = Math.sqrt(x + y);
31178         
31179         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31180         
31181         if(!this.zoomable()){
31182             this.scale = this.startScale;
31183             return;
31184         }
31185         
31186         this.draw();
31187         
31188     },
31189     
31190     onTouchEnd : function(e)
31191     {
31192         this.pinching = false;
31193         this.dragable = false;
31194         
31195     },
31196     
31197     process : function(file, crop)
31198     {
31199         if(this.loadMask){
31200             this.maskEl.mask(this.loadingText);
31201         }
31202         
31203         this.xhr = new XMLHttpRequest();
31204         
31205         file.xhr = this.xhr;
31206
31207         this.xhr.open(this.method, this.url, true);
31208         
31209         var headers = {
31210             "Accept": "application/json",
31211             "Cache-Control": "no-cache",
31212             "X-Requested-With": "XMLHttpRequest"
31213         };
31214         
31215         for (var headerName in headers) {
31216             var headerValue = headers[headerName];
31217             if (headerValue) {
31218                 this.xhr.setRequestHeader(headerName, headerValue);
31219             }
31220         }
31221         
31222         var _this = this;
31223         
31224         this.xhr.onload = function()
31225         {
31226             _this.xhrOnLoad(_this.xhr);
31227         }
31228         
31229         this.xhr.onerror = function()
31230         {
31231             _this.xhrOnError(_this.xhr);
31232         }
31233         
31234         var formData = new FormData();
31235
31236         formData.append('returnHTML', 'NO');
31237         
31238         if(crop){
31239             formData.append('crop', crop);
31240         }
31241         
31242         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31243             formData.append(this.paramName, file, file.name);
31244         }
31245         
31246         if(typeof(file.filename) != 'undefined'){
31247             formData.append('filename', file.filename);
31248         }
31249         
31250         if(typeof(file.mimetype) != 'undefined'){
31251             formData.append('mimetype', file.mimetype);
31252         }
31253         
31254         if(this.fireEvent('arrange', this, formData) != false){
31255             this.xhr.send(formData);
31256         };
31257     },
31258     
31259     xhrOnLoad : function(xhr)
31260     {
31261         if(this.loadMask){
31262             this.maskEl.unmask();
31263         }
31264         
31265         if (xhr.readyState !== 4) {
31266             this.fireEvent('exception', this, xhr);
31267             return;
31268         }
31269
31270         var response = Roo.decode(xhr.responseText);
31271         
31272         if(!response.success){
31273             this.fireEvent('exception', this, xhr);
31274             return;
31275         }
31276         
31277         var response = Roo.decode(xhr.responseText);
31278         
31279         this.fireEvent('upload', this, response);
31280         
31281     },
31282     
31283     xhrOnError : function()
31284     {
31285         if(this.loadMask){
31286             this.maskEl.unmask();
31287         }
31288         
31289         Roo.log('xhr on error');
31290         
31291         var response = Roo.decode(xhr.responseText);
31292           
31293         Roo.log(response);
31294         
31295     },
31296     
31297     prepare : function(file)
31298     {   
31299         if(this.loadMask){
31300             this.maskEl.mask(this.loadingText);
31301         }
31302         
31303         this.file = false;
31304         this.exif = {};
31305         
31306         if(typeof(file) === 'string'){
31307             this.loadCanvas(file);
31308             return;
31309         }
31310         
31311         if(!file || !this.urlAPI){
31312             return;
31313         }
31314         
31315         this.file = file;
31316         this.cropType = file.type;
31317         
31318         var _this = this;
31319         
31320         if(this.fireEvent('prepare', this, this.file) != false){
31321             
31322             var reader = new FileReader();
31323             
31324             reader.onload = function (e) {
31325                 if (e.target.error) {
31326                     Roo.log(e.target.error);
31327                     return;
31328                 }
31329                 
31330                 var buffer = e.target.result,
31331                     dataView = new DataView(buffer),
31332                     offset = 2,
31333                     maxOffset = dataView.byteLength - 4,
31334                     markerBytes,
31335                     markerLength;
31336                 
31337                 if (dataView.getUint16(0) === 0xffd8) {
31338                     while (offset < maxOffset) {
31339                         markerBytes = dataView.getUint16(offset);
31340                         
31341                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31342                             markerLength = dataView.getUint16(offset + 2) + 2;
31343                             if (offset + markerLength > dataView.byteLength) {
31344                                 Roo.log('Invalid meta data: Invalid segment size.');
31345                                 break;
31346                             }
31347                             
31348                             if(markerBytes == 0xffe1){
31349                                 _this.parseExifData(
31350                                     dataView,
31351                                     offset,
31352                                     markerLength
31353                                 );
31354                             }
31355                             
31356                             offset += markerLength;
31357                             
31358                             continue;
31359                         }
31360                         
31361                         break;
31362                     }
31363                     
31364                 }
31365                 
31366                 var url = _this.urlAPI.createObjectURL(_this.file);
31367                 
31368                 _this.loadCanvas(url);
31369                 
31370                 return;
31371             }
31372             
31373             reader.readAsArrayBuffer(this.file);
31374             
31375         }
31376         
31377     },
31378     
31379     parseExifData : function(dataView, offset, length)
31380     {
31381         var tiffOffset = offset + 10,
31382             littleEndian,
31383             dirOffset;
31384     
31385         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31386             // No Exif data, might be XMP data instead
31387             return;
31388         }
31389         
31390         // Check for the ASCII code for "Exif" (0x45786966):
31391         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31392             // No Exif data, might be XMP data instead
31393             return;
31394         }
31395         if (tiffOffset + 8 > dataView.byteLength) {
31396             Roo.log('Invalid Exif data: Invalid segment size.');
31397             return;
31398         }
31399         // Check for the two null bytes:
31400         if (dataView.getUint16(offset + 8) !== 0x0000) {
31401             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31402             return;
31403         }
31404         // Check the byte alignment:
31405         switch (dataView.getUint16(tiffOffset)) {
31406         case 0x4949:
31407             littleEndian = true;
31408             break;
31409         case 0x4D4D:
31410             littleEndian = false;
31411             break;
31412         default:
31413             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31414             return;
31415         }
31416         // Check for the TIFF tag marker (0x002A):
31417         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31418             Roo.log('Invalid Exif data: Missing TIFF marker.');
31419             return;
31420         }
31421         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31422         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31423         
31424         this.parseExifTags(
31425             dataView,
31426             tiffOffset,
31427             tiffOffset + dirOffset,
31428             littleEndian
31429         );
31430     },
31431     
31432     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31433     {
31434         var tagsNumber,
31435             dirEndOffset,
31436             i;
31437         if (dirOffset + 6 > dataView.byteLength) {
31438             Roo.log('Invalid Exif data: Invalid directory offset.');
31439             return;
31440         }
31441         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31442         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31443         if (dirEndOffset + 4 > dataView.byteLength) {
31444             Roo.log('Invalid Exif data: Invalid directory size.');
31445             return;
31446         }
31447         for (i = 0; i < tagsNumber; i += 1) {
31448             this.parseExifTag(
31449                 dataView,
31450                 tiffOffset,
31451                 dirOffset + 2 + 12 * i, // tag offset
31452                 littleEndian
31453             );
31454         }
31455         // Return the offset to the next directory:
31456         return dataView.getUint32(dirEndOffset, littleEndian);
31457     },
31458     
31459     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31460     {
31461         var tag = dataView.getUint16(offset, littleEndian);
31462         
31463         this.exif[tag] = this.getExifValue(
31464             dataView,
31465             tiffOffset,
31466             offset,
31467             dataView.getUint16(offset + 2, littleEndian), // tag type
31468             dataView.getUint32(offset + 4, littleEndian), // tag length
31469             littleEndian
31470         );
31471     },
31472     
31473     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31474     {
31475         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31476             tagSize,
31477             dataOffset,
31478             values,
31479             i,
31480             str,
31481             c;
31482     
31483         if (!tagType) {
31484             Roo.log('Invalid Exif data: Invalid tag type.');
31485             return;
31486         }
31487         
31488         tagSize = tagType.size * length;
31489         // Determine if the value is contained in the dataOffset bytes,
31490         // or if the value at the dataOffset is a pointer to the actual data:
31491         dataOffset = tagSize > 4 ?
31492                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31493         if (dataOffset + tagSize > dataView.byteLength) {
31494             Roo.log('Invalid Exif data: Invalid data offset.');
31495             return;
31496         }
31497         if (length === 1) {
31498             return tagType.getValue(dataView, dataOffset, littleEndian);
31499         }
31500         values = [];
31501         for (i = 0; i < length; i += 1) {
31502             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31503         }
31504         
31505         if (tagType.ascii) {
31506             str = '';
31507             // Concatenate the chars:
31508             for (i = 0; i < values.length; i += 1) {
31509                 c = values[i];
31510                 // Ignore the terminating NULL byte(s):
31511                 if (c === '\u0000') {
31512                     break;
31513                 }
31514                 str += c;
31515             }
31516             return str;
31517         }
31518         return values;
31519     }
31520     
31521 });
31522
31523 Roo.apply(Roo.bootstrap.UploadCropbox, {
31524     tags : {
31525         'Orientation': 0x0112
31526     },
31527     
31528     Orientation: {
31529             1: 0, //'top-left',
31530 //            2: 'top-right',
31531             3: 180, //'bottom-right',
31532 //            4: 'bottom-left',
31533 //            5: 'left-top',
31534             6: 90, //'right-top',
31535 //            7: 'right-bottom',
31536             8: 270 //'left-bottom'
31537     },
31538     
31539     exifTagTypes : {
31540         // byte, 8-bit unsigned int:
31541         1: {
31542             getValue: function (dataView, dataOffset) {
31543                 return dataView.getUint8(dataOffset);
31544             },
31545             size: 1
31546         },
31547         // ascii, 8-bit byte:
31548         2: {
31549             getValue: function (dataView, dataOffset) {
31550                 return String.fromCharCode(dataView.getUint8(dataOffset));
31551             },
31552             size: 1,
31553             ascii: true
31554         },
31555         // short, 16 bit int:
31556         3: {
31557             getValue: function (dataView, dataOffset, littleEndian) {
31558                 return dataView.getUint16(dataOffset, littleEndian);
31559             },
31560             size: 2
31561         },
31562         // long, 32 bit int:
31563         4: {
31564             getValue: function (dataView, dataOffset, littleEndian) {
31565                 return dataView.getUint32(dataOffset, littleEndian);
31566             },
31567             size: 4
31568         },
31569         // rational = two long values, first is numerator, second is denominator:
31570         5: {
31571             getValue: function (dataView, dataOffset, littleEndian) {
31572                 return dataView.getUint32(dataOffset, littleEndian) /
31573                     dataView.getUint32(dataOffset + 4, littleEndian);
31574             },
31575             size: 8
31576         },
31577         // slong, 32 bit signed int:
31578         9: {
31579             getValue: function (dataView, dataOffset, littleEndian) {
31580                 return dataView.getInt32(dataOffset, littleEndian);
31581             },
31582             size: 4
31583         },
31584         // srational, two slongs, first is numerator, second is denominator:
31585         10: {
31586             getValue: function (dataView, dataOffset, littleEndian) {
31587                 return dataView.getInt32(dataOffset, littleEndian) /
31588                     dataView.getInt32(dataOffset + 4, littleEndian);
31589             },
31590             size: 8
31591         }
31592     },
31593     
31594     footer : {
31595         STANDARD : [
31596             {
31597                 tag : 'div',
31598                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31599                 action : 'rotate-left',
31600                 cn : [
31601                     {
31602                         tag : 'button',
31603                         cls : 'btn btn-default',
31604                         html : '<i class="fa fa-undo"></i>'
31605                     }
31606                 ]
31607             },
31608             {
31609                 tag : 'div',
31610                 cls : 'btn-group roo-upload-cropbox-picture',
31611                 action : 'picture',
31612                 cn : [
31613                     {
31614                         tag : 'button',
31615                         cls : 'btn btn-default',
31616                         html : '<i class="fa fa-picture-o"></i>'
31617                     }
31618                 ]
31619             },
31620             {
31621                 tag : 'div',
31622                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31623                 action : 'rotate-right',
31624                 cn : [
31625                     {
31626                         tag : 'button',
31627                         cls : 'btn btn-default',
31628                         html : '<i class="fa fa-repeat"></i>'
31629                     }
31630                 ]
31631             }
31632         ],
31633         DOCUMENT : [
31634             {
31635                 tag : 'div',
31636                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31637                 action : 'rotate-left',
31638                 cn : [
31639                     {
31640                         tag : 'button',
31641                         cls : 'btn btn-default',
31642                         html : '<i class="fa fa-undo"></i>'
31643                     }
31644                 ]
31645             },
31646             {
31647                 tag : 'div',
31648                 cls : 'btn-group roo-upload-cropbox-download',
31649                 action : 'download',
31650                 cn : [
31651                     {
31652                         tag : 'button',
31653                         cls : 'btn btn-default',
31654                         html : '<i class="fa fa-download"></i>'
31655                     }
31656                 ]
31657             },
31658             {
31659                 tag : 'div',
31660                 cls : 'btn-group roo-upload-cropbox-crop',
31661                 action : 'crop',
31662                 cn : [
31663                     {
31664                         tag : 'button',
31665                         cls : 'btn btn-default',
31666                         html : '<i class="fa fa-crop"></i>'
31667                     }
31668                 ]
31669             },
31670             {
31671                 tag : 'div',
31672                 cls : 'btn-group roo-upload-cropbox-trash',
31673                 action : 'trash',
31674                 cn : [
31675                     {
31676                         tag : 'button',
31677                         cls : 'btn btn-default',
31678                         html : '<i class="fa fa-trash"></i>'
31679                     }
31680                 ]
31681             },
31682             {
31683                 tag : 'div',
31684                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31685                 action : 'rotate-right',
31686                 cn : [
31687                     {
31688                         tag : 'button',
31689                         cls : 'btn btn-default',
31690                         html : '<i class="fa fa-repeat"></i>'
31691                     }
31692                 ]
31693             }
31694         ],
31695         ROTATOR : [
31696             {
31697                 tag : 'div',
31698                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31699                 action : 'rotate-left',
31700                 cn : [
31701                     {
31702                         tag : 'button',
31703                         cls : 'btn btn-default',
31704                         html : '<i class="fa fa-undo"></i>'
31705                     }
31706                 ]
31707             },
31708             {
31709                 tag : 'div',
31710                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31711                 action : 'rotate-right',
31712                 cn : [
31713                     {
31714                         tag : 'button',
31715                         cls : 'btn btn-default',
31716                         html : '<i class="fa fa-repeat"></i>'
31717                     }
31718                 ]
31719             }
31720         ]
31721     }
31722 });
31723
31724 /*
31725 * Licence: LGPL
31726 */
31727
31728 /**
31729  * @class Roo.bootstrap.DocumentManager
31730  * @extends Roo.bootstrap.Component
31731  * Bootstrap DocumentManager class
31732  * @cfg {String} paramName default 'imageUpload'
31733  * @cfg {String} toolTipName default 'filename'
31734  * @cfg {String} method default POST
31735  * @cfg {String} url action url
31736  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31737  * @cfg {Boolean} multiple multiple upload default true
31738  * @cfg {Number} thumbSize default 300
31739  * @cfg {String} fieldLabel
31740  * @cfg {Number} labelWidth default 4
31741  * @cfg {String} labelAlign (left|top) default left
31742  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31743 * @cfg {Number} labellg set the width of label (1-12)
31744  * @cfg {Number} labelmd set the width of label (1-12)
31745  * @cfg {Number} labelsm set the width of label (1-12)
31746  * @cfg {Number} labelxs set the width of label (1-12)
31747  * 
31748  * @constructor
31749  * Create a new DocumentManager
31750  * @param {Object} config The config object
31751  */
31752
31753 Roo.bootstrap.DocumentManager = function(config){
31754     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31755     
31756     this.files = [];
31757     this.delegates = [];
31758     
31759     this.addEvents({
31760         /**
31761          * @event initial
31762          * Fire when initial the DocumentManager
31763          * @param {Roo.bootstrap.DocumentManager} this
31764          */
31765         "initial" : true,
31766         /**
31767          * @event inspect
31768          * inspect selected file
31769          * @param {Roo.bootstrap.DocumentManager} this
31770          * @param {File} file
31771          */
31772         "inspect" : true,
31773         /**
31774          * @event exception
31775          * Fire when xhr load exception
31776          * @param {Roo.bootstrap.DocumentManager} this
31777          * @param {XMLHttpRequest} xhr
31778          */
31779         "exception" : true,
31780         /**
31781          * @event afterupload
31782          * Fire when xhr load exception
31783          * @param {Roo.bootstrap.DocumentManager} this
31784          * @param {XMLHttpRequest} xhr
31785          */
31786         "afterupload" : true,
31787         /**
31788          * @event prepare
31789          * prepare the form data
31790          * @param {Roo.bootstrap.DocumentManager} this
31791          * @param {Object} formData
31792          */
31793         "prepare" : true,
31794         /**
31795          * @event remove
31796          * Fire when remove the file
31797          * @param {Roo.bootstrap.DocumentManager} this
31798          * @param {Object} file
31799          */
31800         "remove" : true,
31801         /**
31802          * @event refresh
31803          * Fire after refresh the file
31804          * @param {Roo.bootstrap.DocumentManager} this
31805          */
31806         "refresh" : true,
31807         /**
31808          * @event click
31809          * Fire after click the image
31810          * @param {Roo.bootstrap.DocumentManager} this
31811          * @param {Object} file
31812          */
31813         "click" : true,
31814         /**
31815          * @event edit
31816          * Fire when upload a image and editable set to true
31817          * @param {Roo.bootstrap.DocumentManager} this
31818          * @param {Object} file
31819          */
31820         "edit" : true,
31821         /**
31822          * @event beforeselectfile
31823          * Fire before select file
31824          * @param {Roo.bootstrap.DocumentManager} this
31825          */
31826         "beforeselectfile" : true,
31827         /**
31828          * @event process
31829          * Fire before process file
31830          * @param {Roo.bootstrap.DocumentManager} this
31831          * @param {Object} file
31832          */
31833         "process" : true,
31834         /**
31835          * @event previewrendered
31836          * Fire when preview rendered
31837          * @param {Roo.bootstrap.DocumentManager} this
31838          * @param {Object} file
31839          */
31840         "previewrendered" : true,
31841         /**
31842          */
31843         "previewResize" : true
31844         
31845     });
31846 };
31847
31848 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31849     
31850     boxes : 0,
31851     inputName : '',
31852     thumbSize : 300,
31853     multiple : true,
31854     files : false,
31855     method : 'POST',
31856     url : '',
31857     paramName : 'imageUpload',
31858     toolTipName : 'filename',
31859     fieldLabel : '',
31860     labelWidth : 4,
31861     labelAlign : 'left',
31862     editable : true,
31863     delegates : false,
31864     xhr : false, 
31865     
31866     labellg : 0,
31867     labelmd : 0,
31868     labelsm : 0,
31869     labelxs : 0,
31870     
31871     getAutoCreate : function()
31872     {   
31873         var managerWidget = {
31874             tag : 'div',
31875             cls : 'roo-document-manager',
31876             cn : [
31877                 {
31878                     tag : 'input',
31879                     cls : 'roo-document-manager-selector',
31880                     type : 'file'
31881                 },
31882                 {
31883                     tag : 'div',
31884                     cls : 'roo-document-manager-uploader',
31885                     cn : [
31886                         {
31887                             tag : 'div',
31888                             cls : 'roo-document-manager-upload-btn',
31889                             html : '<i class="fa fa-plus"></i>'
31890                         }
31891                     ]
31892                     
31893                 }
31894             ]
31895         };
31896         
31897         var content = [
31898             {
31899                 tag : 'div',
31900                 cls : 'column col-md-12',
31901                 cn : managerWidget
31902             }
31903         ];
31904         
31905         if(this.fieldLabel.length){
31906             
31907             content = [
31908                 {
31909                     tag : 'div',
31910                     cls : 'column col-md-12',
31911                     html : this.fieldLabel
31912                 },
31913                 {
31914                     tag : 'div',
31915                     cls : 'column col-md-12',
31916                     cn : managerWidget
31917                 }
31918             ];
31919
31920             if(this.labelAlign == 'left'){
31921                 content = [
31922                     {
31923                         tag : 'div',
31924                         cls : 'column',
31925                         html : this.fieldLabel
31926                     },
31927                     {
31928                         tag : 'div',
31929                         cls : 'column',
31930                         cn : managerWidget
31931                     }
31932                 ];
31933                 
31934                 if(this.labelWidth > 12){
31935                     content[0].style = "width: " + this.labelWidth + 'px';
31936                 }
31937
31938                 if(this.labelWidth < 13 && this.labelmd == 0){
31939                     this.labelmd = this.labelWidth;
31940                 }
31941
31942                 if(this.labellg > 0){
31943                     content[0].cls += ' col-lg-' + this.labellg;
31944                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31945                 }
31946
31947                 if(this.labelmd > 0){
31948                     content[0].cls += ' col-md-' + this.labelmd;
31949                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31950                 }
31951
31952                 if(this.labelsm > 0){
31953                     content[0].cls += ' col-sm-' + this.labelsm;
31954                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31955                 }
31956
31957                 if(this.labelxs > 0){
31958                     content[0].cls += ' col-xs-' + this.labelxs;
31959                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31960                 }
31961                 
31962             }
31963         }
31964         
31965         var cfg = {
31966             tag : 'div',
31967             cls : 'row clearfix',
31968             cn : content
31969         };
31970         
31971         return cfg;
31972         
31973     },
31974     
31975     initEvents : function()
31976     {
31977         this.managerEl = this.el.select('.roo-document-manager', true).first();
31978         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31979         
31980         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31981         this.selectorEl.hide();
31982         
31983         if(this.multiple){
31984             this.selectorEl.attr('multiple', 'multiple');
31985         }
31986         
31987         this.selectorEl.on('change', this.onFileSelected, this);
31988         
31989         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31990         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31991         
31992         this.uploader.on('click', this.onUploaderClick, this);
31993         
31994         this.renderProgressDialog();
31995         
31996         var _this = this;
31997         
31998         window.addEventListener("resize", function() { _this.refresh(); } );
31999         
32000         this.fireEvent('initial', this);
32001     },
32002     
32003     renderProgressDialog : function()
32004     {
32005         var _this = this;
32006         
32007         this.progressDialog = new Roo.bootstrap.Modal({
32008             cls : 'roo-document-manager-progress-dialog',
32009             allow_close : false,
32010             animate : false,
32011             title : '',
32012             buttons : [
32013                 {
32014                     name  :'cancel',
32015                     weight : 'danger',
32016                     html : 'Cancel'
32017                 }
32018             ], 
32019             listeners : { 
32020                 btnclick : function() {
32021                     _this.uploadCancel();
32022                     this.hide();
32023                 }
32024             }
32025         });
32026          
32027         this.progressDialog.render(Roo.get(document.body));
32028          
32029         this.progress = new Roo.bootstrap.Progress({
32030             cls : 'roo-document-manager-progress',
32031             active : true,
32032             striped : true
32033         });
32034         
32035         this.progress.render(this.progressDialog.getChildContainer());
32036         
32037         this.progressBar = new Roo.bootstrap.ProgressBar({
32038             cls : 'roo-document-manager-progress-bar',
32039             aria_valuenow : 0,
32040             aria_valuemin : 0,
32041             aria_valuemax : 12,
32042             panel : 'success'
32043         });
32044         
32045         this.progressBar.render(this.progress.getChildContainer());
32046     },
32047     
32048     onUploaderClick : function(e)
32049     {
32050         e.preventDefault();
32051      
32052         if(this.fireEvent('beforeselectfile', this) != false){
32053             this.selectorEl.dom.click();
32054         }
32055         
32056     },
32057     
32058     onFileSelected : function(e)
32059     {
32060         e.preventDefault();
32061         
32062         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32063             return;
32064         }
32065         
32066         Roo.each(this.selectorEl.dom.files, function(file){
32067             if(this.fireEvent('inspect', this, file) != false){
32068                 this.files.push(file);
32069             }
32070         }, this);
32071         
32072         this.queue();
32073         
32074     },
32075     
32076     queue : function()
32077     {
32078         this.selectorEl.dom.value = '';
32079         
32080         if(!this.files || !this.files.length){
32081             return;
32082         }
32083         
32084         if(this.boxes > 0 && this.files.length > this.boxes){
32085             this.files = this.files.slice(0, this.boxes);
32086         }
32087         
32088         this.uploader.show();
32089         
32090         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32091             this.uploader.hide();
32092         }
32093         
32094         var _this = this;
32095         
32096         var files = [];
32097         
32098         var docs = [];
32099         
32100         Roo.each(this.files, function(file){
32101             
32102             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32103                 var f = this.renderPreview(file);
32104                 files.push(f);
32105                 return;
32106             }
32107             
32108             if(file.type.indexOf('image') != -1){
32109                 this.delegates.push(
32110                     (function(){
32111                         _this.process(file);
32112                     }).createDelegate(this)
32113                 );
32114         
32115                 return;
32116             }
32117             
32118             docs.push(
32119                 (function(){
32120                     _this.process(file);
32121                 }).createDelegate(this)
32122             );
32123             
32124         }, this);
32125         
32126         this.files = files;
32127         
32128         this.delegates = this.delegates.concat(docs);
32129         
32130         if(!this.delegates.length){
32131             this.refresh();
32132             return;
32133         }
32134         
32135         this.progressBar.aria_valuemax = this.delegates.length;
32136         
32137         this.arrange();
32138         
32139         return;
32140     },
32141     
32142     arrange : function()
32143     {
32144         if(!this.delegates.length){
32145             this.progressDialog.hide();
32146             this.refresh();
32147             return;
32148         }
32149         
32150         var delegate = this.delegates.shift();
32151         
32152         this.progressDialog.show();
32153         
32154         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32155         
32156         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32157         
32158         delegate();
32159     },
32160     
32161     refresh : function()
32162     {
32163         this.uploader.show();
32164         
32165         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32166             this.uploader.hide();
32167         }
32168         
32169         Roo.isTouch ? this.closable(false) : this.closable(true);
32170         
32171         this.fireEvent('refresh', this);
32172     },
32173     
32174     onRemove : function(e, el, o)
32175     {
32176         e.preventDefault();
32177         
32178         this.fireEvent('remove', this, o);
32179         
32180     },
32181     
32182     remove : function(o)
32183     {
32184         var files = [];
32185         
32186         Roo.each(this.files, function(file){
32187             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32188                 files.push(file);
32189                 return;
32190             }
32191
32192             o.target.remove();
32193
32194         }, this);
32195         
32196         this.files = files;
32197         
32198         this.refresh();
32199     },
32200     
32201     clear : function()
32202     {
32203         Roo.each(this.files, function(file){
32204             if(!file.target){
32205                 return;
32206             }
32207             
32208             file.target.remove();
32209
32210         }, this);
32211         
32212         this.files = [];
32213         
32214         this.refresh();
32215     },
32216     
32217     onClick : function(e, el, o)
32218     {
32219         e.preventDefault();
32220         
32221         this.fireEvent('click', this, o);
32222         
32223     },
32224     
32225     closable : function(closable)
32226     {
32227         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32228             
32229             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32230             
32231             if(closable){
32232                 el.show();
32233                 return;
32234             }
32235             
32236             el.hide();
32237             
32238         }, this);
32239     },
32240     
32241     xhrOnLoad : function(xhr)
32242     {
32243         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32244             el.remove();
32245         }, this);
32246         
32247         if (xhr.readyState !== 4) {
32248             this.arrange();
32249             this.fireEvent('exception', this, xhr);
32250             return;
32251         }
32252
32253         var response = Roo.decode(xhr.responseText);
32254         
32255         if(!response.success){
32256             this.arrange();
32257             this.fireEvent('exception', this, xhr);
32258             return;
32259         }
32260         
32261         var file = this.renderPreview(response.data);
32262         
32263         this.files.push(file);
32264         
32265         this.arrange();
32266         
32267         this.fireEvent('afterupload', this, xhr);
32268         
32269     },
32270     
32271     xhrOnError : function(xhr)
32272     {
32273         Roo.log('xhr on error');
32274         
32275         var response = Roo.decode(xhr.responseText);
32276           
32277         Roo.log(response);
32278         
32279         this.arrange();
32280     },
32281     
32282     process : function(file)
32283     {
32284         if(this.fireEvent('process', this, file) !== false){
32285             if(this.editable && file.type.indexOf('image') != -1){
32286                 this.fireEvent('edit', this, file);
32287                 return;
32288             }
32289
32290             this.uploadStart(file, false);
32291
32292             return;
32293         }
32294         
32295     },
32296     
32297     uploadStart : function(file, crop)
32298     {
32299         this.xhr = new XMLHttpRequest();
32300         
32301         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32302             this.arrange();
32303             return;
32304         }
32305         
32306         file.xhr = this.xhr;
32307             
32308         this.managerEl.createChild({
32309             tag : 'div',
32310             cls : 'roo-document-manager-loading',
32311             cn : [
32312                 {
32313                     tag : 'div',
32314                     tooltip : file.name,
32315                     cls : 'roo-document-manager-thumb',
32316                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32317                 }
32318             ]
32319
32320         });
32321
32322         this.xhr.open(this.method, this.url, true);
32323         
32324         var headers = {
32325             "Accept": "application/json",
32326             "Cache-Control": "no-cache",
32327             "X-Requested-With": "XMLHttpRequest"
32328         };
32329         
32330         for (var headerName in headers) {
32331             var headerValue = headers[headerName];
32332             if (headerValue) {
32333                 this.xhr.setRequestHeader(headerName, headerValue);
32334             }
32335         }
32336         
32337         var _this = this;
32338         
32339         this.xhr.onload = function()
32340         {
32341             _this.xhrOnLoad(_this.xhr);
32342         }
32343         
32344         this.xhr.onerror = function()
32345         {
32346             _this.xhrOnError(_this.xhr);
32347         }
32348         
32349         var formData = new FormData();
32350
32351         formData.append('returnHTML', 'NO');
32352         
32353         if(crop){
32354             formData.append('crop', crop);
32355         }
32356         
32357         formData.append(this.paramName, file, file.name);
32358         
32359         var options = {
32360             file : file, 
32361             manually : false
32362         };
32363         
32364         if(this.fireEvent('prepare', this, formData, options) != false){
32365             
32366             if(options.manually){
32367                 return;
32368             }
32369             
32370             this.xhr.send(formData);
32371             return;
32372         };
32373         
32374         this.uploadCancel();
32375     },
32376     
32377     uploadCancel : function()
32378     {
32379         if (this.xhr) {
32380             this.xhr.abort();
32381         }
32382         
32383         this.delegates = [];
32384         
32385         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32386             el.remove();
32387         }, this);
32388         
32389         this.arrange();
32390     },
32391     
32392     renderPreview : function(file)
32393     {
32394         if(typeof(file.target) != 'undefined' && file.target){
32395             return file;
32396         }
32397         
32398         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32399         
32400         var previewEl = this.managerEl.createChild({
32401             tag : 'div',
32402             cls : 'roo-document-manager-preview',
32403             cn : [
32404                 {
32405                     tag : 'div',
32406                     tooltip : file[this.toolTipName],
32407                     cls : 'roo-document-manager-thumb',
32408                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32409                 },
32410                 {
32411                     tag : 'button',
32412                     cls : 'close',
32413                     html : '<i class="fa fa-times-circle"></i>'
32414                 }
32415             ]
32416         });
32417
32418         var close = previewEl.select('button.close', true).first();
32419
32420         close.on('click', this.onRemove, this, file);
32421
32422         file.target = previewEl;
32423
32424         var image = previewEl.select('img', true).first();
32425         
32426         var _this = this;
32427         
32428         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32429         
32430         image.on('click', this.onClick, this, file);
32431         
32432         this.fireEvent('previewrendered', this, file);
32433         
32434         return file;
32435         
32436     },
32437     
32438     onPreviewLoad : function(file, image)
32439     {
32440         if(typeof(file.target) == 'undefined' || !file.target){
32441             return;
32442         }
32443         
32444         var width = image.dom.naturalWidth || image.dom.width;
32445         var height = image.dom.naturalHeight || image.dom.height;
32446         
32447         if(!this.previewResize) {
32448             return;
32449         }
32450         
32451         if(width > height){
32452             file.target.addClass('wide');
32453             return;
32454         }
32455         
32456         file.target.addClass('tall');
32457         return;
32458         
32459     },
32460     
32461     uploadFromSource : function(file, crop)
32462     {
32463         this.xhr = new XMLHttpRequest();
32464         
32465         this.managerEl.createChild({
32466             tag : 'div',
32467             cls : 'roo-document-manager-loading',
32468             cn : [
32469                 {
32470                     tag : 'div',
32471                     tooltip : file.name,
32472                     cls : 'roo-document-manager-thumb',
32473                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32474                 }
32475             ]
32476
32477         });
32478
32479         this.xhr.open(this.method, this.url, true);
32480         
32481         var headers = {
32482             "Accept": "application/json",
32483             "Cache-Control": "no-cache",
32484             "X-Requested-With": "XMLHttpRequest"
32485         };
32486         
32487         for (var headerName in headers) {
32488             var headerValue = headers[headerName];
32489             if (headerValue) {
32490                 this.xhr.setRequestHeader(headerName, headerValue);
32491             }
32492         }
32493         
32494         var _this = this;
32495         
32496         this.xhr.onload = function()
32497         {
32498             _this.xhrOnLoad(_this.xhr);
32499         }
32500         
32501         this.xhr.onerror = function()
32502         {
32503             _this.xhrOnError(_this.xhr);
32504         }
32505         
32506         var formData = new FormData();
32507
32508         formData.append('returnHTML', 'NO');
32509         
32510         formData.append('crop', crop);
32511         
32512         if(typeof(file.filename) != 'undefined'){
32513             formData.append('filename', file.filename);
32514         }
32515         
32516         if(typeof(file.mimetype) != 'undefined'){
32517             formData.append('mimetype', file.mimetype);
32518         }
32519         
32520         Roo.log(formData);
32521         
32522         if(this.fireEvent('prepare', this, formData) != false){
32523             this.xhr.send(formData);
32524         };
32525     }
32526 });
32527
32528 /*
32529 * Licence: LGPL
32530 */
32531
32532 /**
32533  * @class Roo.bootstrap.DocumentViewer
32534  * @extends Roo.bootstrap.Component
32535  * Bootstrap DocumentViewer class
32536  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32537  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32538  * 
32539  * @constructor
32540  * Create a new DocumentViewer
32541  * @param {Object} config The config object
32542  */
32543
32544 Roo.bootstrap.DocumentViewer = function(config){
32545     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32546     
32547     this.addEvents({
32548         /**
32549          * @event initial
32550          * Fire after initEvent
32551          * @param {Roo.bootstrap.DocumentViewer} this
32552          */
32553         "initial" : true,
32554         /**
32555          * @event click
32556          * Fire after click
32557          * @param {Roo.bootstrap.DocumentViewer} this
32558          */
32559         "click" : true,
32560         /**
32561          * @event download
32562          * Fire after download button
32563          * @param {Roo.bootstrap.DocumentViewer} this
32564          */
32565         "download" : true,
32566         /**
32567          * @event trash
32568          * Fire after trash button
32569          * @param {Roo.bootstrap.DocumentViewer} this
32570          */
32571         "trash" : true
32572         
32573     });
32574 };
32575
32576 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32577     
32578     showDownload : true,
32579     
32580     showTrash : true,
32581     
32582     getAutoCreate : function()
32583     {
32584         var cfg = {
32585             tag : 'div',
32586             cls : 'roo-document-viewer',
32587             cn : [
32588                 {
32589                     tag : 'div',
32590                     cls : 'roo-document-viewer-body',
32591                     cn : [
32592                         {
32593                             tag : 'div',
32594                             cls : 'roo-document-viewer-thumb',
32595                             cn : [
32596                                 {
32597                                     tag : 'img',
32598                                     cls : 'roo-document-viewer-image'
32599                                 }
32600                             ]
32601                         }
32602                     ]
32603                 },
32604                 {
32605                     tag : 'div',
32606                     cls : 'roo-document-viewer-footer',
32607                     cn : {
32608                         tag : 'div',
32609                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32610                         cn : [
32611                             {
32612                                 tag : 'div',
32613                                 cls : 'btn-group roo-document-viewer-download',
32614                                 cn : [
32615                                     {
32616                                         tag : 'button',
32617                                         cls : 'btn btn-default',
32618                                         html : '<i class="fa fa-download"></i>'
32619                                     }
32620                                 ]
32621                             },
32622                             {
32623                                 tag : 'div',
32624                                 cls : 'btn-group roo-document-viewer-trash',
32625                                 cn : [
32626                                     {
32627                                         tag : 'button',
32628                                         cls : 'btn btn-default',
32629                                         html : '<i class="fa fa-trash"></i>'
32630                                     }
32631                                 ]
32632                             }
32633                         ]
32634                     }
32635                 }
32636             ]
32637         };
32638         
32639         return cfg;
32640     },
32641     
32642     initEvents : function()
32643     {
32644         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32645         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32646         
32647         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32648         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32649         
32650         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32651         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32652         
32653         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32654         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32655         
32656         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32657         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32658         
32659         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32660         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32661         
32662         this.bodyEl.on('click', this.onClick, this);
32663         this.downloadBtn.on('click', this.onDownload, this);
32664         this.trashBtn.on('click', this.onTrash, this);
32665         
32666         this.downloadBtn.hide();
32667         this.trashBtn.hide();
32668         
32669         if(this.showDownload){
32670             this.downloadBtn.show();
32671         }
32672         
32673         if(this.showTrash){
32674             this.trashBtn.show();
32675         }
32676         
32677         if(!this.showDownload && !this.showTrash) {
32678             this.footerEl.hide();
32679         }
32680         
32681     },
32682     
32683     initial : function()
32684     {
32685         this.fireEvent('initial', this);
32686         
32687     },
32688     
32689     onClick : function(e)
32690     {
32691         e.preventDefault();
32692         
32693         this.fireEvent('click', this);
32694     },
32695     
32696     onDownload : function(e)
32697     {
32698         e.preventDefault();
32699         
32700         this.fireEvent('download', this);
32701     },
32702     
32703     onTrash : function(e)
32704     {
32705         e.preventDefault();
32706         
32707         this.fireEvent('trash', this);
32708     }
32709     
32710 });
32711 /*
32712  * - LGPL
32713  *
32714  * nav progress bar
32715  * 
32716  */
32717
32718 /**
32719  * @class Roo.bootstrap.NavProgressBar
32720  * @extends Roo.bootstrap.Component
32721  * Bootstrap NavProgressBar class
32722  * 
32723  * @constructor
32724  * Create a new nav progress bar
32725  * @param {Object} config The config object
32726  */
32727
32728 Roo.bootstrap.NavProgressBar = function(config){
32729     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32730
32731     this.bullets = this.bullets || [];
32732    
32733 //    Roo.bootstrap.NavProgressBar.register(this);
32734      this.addEvents({
32735         /**
32736              * @event changed
32737              * Fires when the active item changes
32738              * @param {Roo.bootstrap.NavProgressBar} this
32739              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32740              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32741          */
32742         'changed': true
32743      });
32744     
32745 };
32746
32747 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32748     
32749     bullets : [],
32750     barItems : [],
32751     
32752     getAutoCreate : function()
32753     {
32754         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32755         
32756         cfg = {
32757             tag : 'div',
32758             cls : 'roo-navigation-bar-group',
32759             cn : [
32760                 {
32761                     tag : 'div',
32762                     cls : 'roo-navigation-top-bar'
32763                 },
32764                 {
32765                     tag : 'div',
32766                     cls : 'roo-navigation-bullets-bar',
32767                     cn : [
32768                         {
32769                             tag : 'ul',
32770                             cls : 'roo-navigation-bar'
32771                         }
32772                     ]
32773                 },
32774                 
32775                 {
32776                     tag : 'div',
32777                     cls : 'roo-navigation-bottom-bar'
32778                 }
32779             ]
32780             
32781         };
32782         
32783         return cfg;
32784         
32785     },
32786     
32787     initEvents: function() 
32788     {
32789         
32790     },
32791     
32792     onRender : function(ct, position) 
32793     {
32794         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32795         
32796         if(this.bullets.length){
32797             Roo.each(this.bullets, function(b){
32798                this.addItem(b);
32799             }, this);
32800         }
32801         
32802         this.format();
32803         
32804     },
32805     
32806     addItem : function(cfg)
32807     {
32808         var item = new Roo.bootstrap.NavProgressItem(cfg);
32809         
32810         item.parentId = this.id;
32811         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32812         
32813         if(cfg.html){
32814             var top = new Roo.bootstrap.Element({
32815                 tag : 'div',
32816                 cls : 'roo-navigation-bar-text'
32817             });
32818             
32819             var bottom = new Roo.bootstrap.Element({
32820                 tag : 'div',
32821                 cls : 'roo-navigation-bar-text'
32822             });
32823             
32824             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32825             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32826             
32827             var topText = new Roo.bootstrap.Element({
32828                 tag : 'span',
32829                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32830             });
32831             
32832             var bottomText = new Roo.bootstrap.Element({
32833                 tag : 'span',
32834                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32835             });
32836             
32837             topText.onRender(top.el, null);
32838             bottomText.onRender(bottom.el, null);
32839             
32840             item.topEl = top;
32841             item.bottomEl = bottom;
32842         }
32843         
32844         this.barItems.push(item);
32845         
32846         return item;
32847     },
32848     
32849     getActive : function()
32850     {
32851         var active = false;
32852         
32853         Roo.each(this.barItems, function(v){
32854             
32855             if (!v.isActive()) {
32856                 return;
32857             }
32858             
32859             active = v;
32860             return false;
32861             
32862         });
32863         
32864         return active;
32865     },
32866     
32867     setActiveItem : function(item)
32868     {
32869         var prev = false;
32870         
32871         Roo.each(this.barItems, function(v){
32872             if (v.rid == item.rid) {
32873                 return ;
32874             }
32875             
32876             if (v.isActive()) {
32877                 v.setActive(false);
32878                 prev = v;
32879             }
32880         });
32881
32882         item.setActive(true);
32883         
32884         this.fireEvent('changed', this, item, prev);
32885     },
32886     
32887     getBarItem: function(rid)
32888     {
32889         var ret = false;
32890         
32891         Roo.each(this.barItems, function(e) {
32892             if (e.rid != rid) {
32893                 return;
32894             }
32895             
32896             ret =  e;
32897             return false;
32898         });
32899         
32900         return ret;
32901     },
32902     
32903     indexOfItem : function(item)
32904     {
32905         var index = false;
32906         
32907         Roo.each(this.barItems, function(v, i){
32908             
32909             if (v.rid != item.rid) {
32910                 return;
32911             }
32912             
32913             index = i;
32914             return false
32915         });
32916         
32917         return index;
32918     },
32919     
32920     setActiveNext : function()
32921     {
32922         var i = this.indexOfItem(this.getActive());
32923         
32924         if (i > this.barItems.length) {
32925             return;
32926         }
32927         
32928         this.setActiveItem(this.barItems[i+1]);
32929     },
32930     
32931     setActivePrev : function()
32932     {
32933         var i = this.indexOfItem(this.getActive());
32934         
32935         if (i  < 1) {
32936             return;
32937         }
32938         
32939         this.setActiveItem(this.barItems[i-1]);
32940     },
32941     
32942     format : function()
32943     {
32944         if(!this.barItems.length){
32945             return;
32946         }
32947      
32948         var width = 100 / this.barItems.length;
32949         
32950         Roo.each(this.barItems, function(i){
32951             i.el.setStyle('width', width + '%');
32952             i.topEl.el.setStyle('width', width + '%');
32953             i.bottomEl.el.setStyle('width', width + '%');
32954         }, this);
32955         
32956     }
32957     
32958 });
32959 /*
32960  * - LGPL
32961  *
32962  * Nav Progress Item
32963  * 
32964  */
32965
32966 /**
32967  * @class Roo.bootstrap.NavProgressItem
32968  * @extends Roo.bootstrap.Component
32969  * Bootstrap NavProgressItem class
32970  * @cfg {String} rid the reference id
32971  * @cfg {Boolean} active (true|false) Is item active default false
32972  * @cfg {Boolean} disabled (true|false) Is item active default false
32973  * @cfg {String} html
32974  * @cfg {String} position (top|bottom) text position default bottom
32975  * @cfg {String} icon show icon instead of number
32976  * 
32977  * @constructor
32978  * Create a new NavProgressItem
32979  * @param {Object} config The config object
32980  */
32981 Roo.bootstrap.NavProgressItem = function(config){
32982     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32983     this.addEvents({
32984         // raw events
32985         /**
32986          * @event click
32987          * The raw click event for the entire grid.
32988          * @param {Roo.bootstrap.NavProgressItem} this
32989          * @param {Roo.EventObject} e
32990          */
32991         "click" : true
32992     });
32993    
32994 };
32995
32996 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32997     
32998     rid : '',
32999     active : false,
33000     disabled : false,
33001     html : '',
33002     position : 'bottom',
33003     icon : false,
33004     
33005     getAutoCreate : function()
33006     {
33007         var iconCls = 'roo-navigation-bar-item-icon';
33008         
33009         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33010         
33011         var cfg = {
33012             tag: 'li',
33013             cls: 'roo-navigation-bar-item',
33014             cn : [
33015                 {
33016                     tag : 'i',
33017                     cls : iconCls
33018                 }
33019             ]
33020         };
33021         
33022         if(this.active){
33023             cfg.cls += ' active';
33024         }
33025         if(this.disabled){
33026             cfg.cls += ' disabled';
33027         }
33028         
33029         return cfg;
33030     },
33031     
33032     disable : function()
33033     {
33034         this.setDisabled(true);
33035     },
33036     
33037     enable : function()
33038     {
33039         this.setDisabled(false);
33040     },
33041     
33042     initEvents: function() 
33043     {
33044         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33045         
33046         this.iconEl.on('click', this.onClick, this);
33047     },
33048     
33049     onClick : function(e)
33050     {
33051         e.preventDefault();
33052         
33053         if(this.disabled){
33054             return;
33055         }
33056         
33057         if(this.fireEvent('click', this, e) === false){
33058             return;
33059         };
33060         
33061         this.parent().setActiveItem(this);
33062     },
33063     
33064     isActive: function () 
33065     {
33066         return this.active;
33067     },
33068     
33069     setActive : function(state)
33070     {
33071         if(this.active == state){
33072             return;
33073         }
33074         
33075         this.active = state;
33076         
33077         if (state) {
33078             this.el.addClass('active');
33079             return;
33080         }
33081         
33082         this.el.removeClass('active');
33083         
33084         return;
33085     },
33086     
33087     setDisabled : function(state)
33088     {
33089         if(this.disabled == state){
33090             return;
33091         }
33092         
33093         this.disabled = state;
33094         
33095         if (state) {
33096             this.el.addClass('disabled');
33097             return;
33098         }
33099         
33100         this.el.removeClass('disabled');
33101     },
33102     
33103     tooltipEl : function()
33104     {
33105         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33106     }
33107 });
33108  
33109
33110  /*
33111  * - LGPL
33112  *
33113  * FieldLabel
33114  * 
33115  */
33116
33117 /**
33118  * @class Roo.bootstrap.FieldLabel
33119  * @extends Roo.bootstrap.Component
33120  * Bootstrap FieldLabel class
33121  * @cfg {String} html contents of the element
33122  * @cfg {String} tag tag of the element default label
33123  * @cfg {String} cls class of the element
33124  * @cfg {String} target label target 
33125  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33126  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33127  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33128  * @cfg {String} iconTooltip default "This field is required"
33129  * @cfg {String} indicatorpos (left|right) default left
33130  * 
33131  * @constructor
33132  * Create a new FieldLabel
33133  * @param {Object} config The config object
33134  */
33135
33136 Roo.bootstrap.FieldLabel = function(config){
33137     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33138     
33139     this.addEvents({
33140             /**
33141              * @event invalid
33142              * Fires after the field has been marked as invalid.
33143              * @param {Roo.form.FieldLabel} this
33144              * @param {String} msg The validation message
33145              */
33146             invalid : true,
33147             /**
33148              * @event valid
33149              * Fires after the field has been validated with no errors.
33150              * @param {Roo.form.FieldLabel} this
33151              */
33152             valid : true
33153         });
33154 };
33155
33156 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33157     
33158     tag: 'label',
33159     cls: '',
33160     html: '',
33161     target: '',
33162     allowBlank : true,
33163     invalidClass : 'has-warning',
33164     validClass : 'has-success',
33165     iconTooltip : 'This field is required',
33166     indicatorpos : 'left',
33167     
33168     getAutoCreate : function(){
33169         
33170         var cls = "";
33171         if (!this.allowBlank) {
33172             cls  = "visible";
33173         }
33174         
33175         var cfg = {
33176             tag : this.tag,
33177             cls : 'roo-bootstrap-field-label ' + this.cls,
33178             for : this.target,
33179             cn : [
33180                 {
33181                     tag : 'i',
33182                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33183                     tooltip : this.iconTooltip
33184                 },
33185                 {
33186                     tag : 'span',
33187                     html : this.html
33188                 }
33189             ] 
33190         };
33191         
33192         if(this.indicatorpos == 'right'){
33193             var cfg = {
33194                 tag : this.tag,
33195                 cls : 'roo-bootstrap-field-label ' + this.cls,
33196                 for : this.target,
33197                 cn : [
33198                     {
33199                         tag : 'span',
33200                         html : this.html
33201                     },
33202                     {
33203                         tag : 'i',
33204                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33205                         tooltip : this.iconTooltip
33206                     }
33207                 ] 
33208             };
33209         }
33210         
33211         return cfg;
33212     },
33213     
33214     initEvents: function() 
33215     {
33216         Roo.bootstrap.Element.superclass.initEvents.call(this);
33217         
33218         this.indicator = this.indicatorEl();
33219         
33220         if(this.indicator){
33221             this.indicator.removeClass('visible');
33222             this.indicator.addClass('invisible');
33223         }
33224         
33225         Roo.bootstrap.FieldLabel.register(this);
33226     },
33227     
33228     indicatorEl : function()
33229     {
33230         var indicator = this.el.select('i.roo-required-indicator',true).first();
33231         
33232         if(!indicator){
33233             return false;
33234         }
33235         
33236         return indicator;
33237         
33238     },
33239     
33240     /**
33241      * Mark this field as valid
33242      */
33243     markValid : function()
33244     {
33245         if(this.indicator){
33246             this.indicator.removeClass('visible');
33247             this.indicator.addClass('invisible');
33248         }
33249         if (Roo.bootstrap.version == 3) {
33250             this.el.removeClass(this.invalidClass);
33251             this.el.addClass(this.validClass);
33252         } else {
33253             this.el.removeClass('is-invalid');
33254             this.el.addClass('is-valid');
33255         }
33256         
33257         
33258         this.fireEvent('valid', this);
33259     },
33260     
33261     /**
33262      * Mark this field as invalid
33263      * @param {String} msg The validation message
33264      */
33265     markInvalid : function(msg)
33266     {
33267         if(this.indicator){
33268             this.indicator.removeClass('invisible');
33269             this.indicator.addClass('visible');
33270         }
33271           if (Roo.bootstrap.version == 3) {
33272             this.el.removeClass(this.validClass);
33273             this.el.addClass(this.invalidClass);
33274         } else {
33275             this.el.removeClass('is-valid');
33276             this.el.addClass('is-invalid');
33277         }
33278         
33279         
33280         this.fireEvent('invalid', this, msg);
33281     }
33282     
33283    
33284 });
33285
33286 Roo.apply(Roo.bootstrap.FieldLabel, {
33287     
33288     groups: {},
33289     
33290      /**
33291     * register a FieldLabel Group
33292     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33293     */
33294     register : function(label)
33295     {
33296         if(this.groups.hasOwnProperty(label.target)){
33297             return;
33298         }
33299      
33300         this.groups[label.target] = label;
33301         
33302     },
33303     /**
33304     * fetch a FieldLabel Group based on the target
33305     * @param {string} target
33306     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33307     */
33308     get: function(target) {
33309         if (typeof(this.groups[target]) == 'undefined') {
33310             return false;
33311         }
33312         
33313         return this.groups[target] ;
33314     }
33315 });
33316
33317  
33318
33319  /*
33320  * - LGPL
33321  *
33322  * page DateSplitField.
33323  * 
33324  */
33325
33326
33327 /**
33328  * @class Roo.bootstrap.DateSplitField
33329  * @extends Roo.bootstrap.Component
33330  * Bootstrap DateSplitField class
33331  * @cfg {string} fieldLabel - the label associated
33332  * @cfg {Number} labelWidth set the width of label (0-12)
33333  * @cfg {String} labelAlign (top|left)
33334  * @cfg {Boolean} dayAllowBlank (true|false) default false
33335  * @cfg {Boolean} monthAllowBlank (true|false) default false
33336  * @cfg {Boolean} yearAllowBlank (true|false) default false
33337  * @cfg {string} dayPlaceholder 
33338  * @cfg {string} monthPlaceholder
33339  * @cfg {string} yearPlaceholder
33340  * @cfg {string} dayFormat default 'd'
33341  * @cfg {string} monthFormat default 'm'
33342  * @cfg {string} yearFormat default 'Y'
33343  * @cfg {Number} labellg set the width of label (1-12)
33344  * @cfg {Number} labelmd set the width of label (1-12)
33345  * @cfg {Number} labelsm set the width of label (1-12)
33346  * @cfg {Number} labelxs set the width of label (1-12)
33347
33348  *     
33349  * @constructor
33350  * Create a new DateSplitField
33351  * @param {Object} config The config object
33352  */
33353
33354 Roo.bootstrap.DateSplitField = function(config){
33355     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33356     
33357     this.addEvents({
33358         // raw events
33359          /**
33360          * @event years
33361          * getting the data of years
33362          * @param {Roo.bootstrap.DateSplitField} this
33363          * @param {Object} years
33364          */
33365         "years" : true,
33366         /**
33367          * @event days
33368          * getting the data of days
33369          * @param {Roo.bootstrap.DateSplitField} this
33370          * @param {Object} days
33371          */
33372         "days" : true,
33373         /**
33374          * @event invalid
33375          * Fires after the field has been marked as invalid.
33376          * @param {Roo.form.Field} this
33377          * @param {String} msg The validation message
33378          */
33379         invalid : true,
33380        /**
33381          * @event valid
33382          * Fires after the field has been validated with no errors.
33383          * @param {Roo.form.Field} this
33384          */
33385         valid : true
33386     });
33387 };
33388
33389 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33390     
33391     fieldLabel : '',
33392     labelAlign : 'top',
33393     labelWidth : 3,
33394     dayAllowBlank : false,
33395     monthAllowBlank : false,
33396     yearAllowBlank : false,
33397     dayPlaceholder : '',
33398     monthPlaceholder : '',
33399     yearPlaceholder : '',
33400     dayFormat : 'd',
33401     monthFormat : 'm',
33402     yearFormat : 'Y',
33403     isFormField : true,
33404     labellg : 0,
33405     labelmd : 0,
33406     labelsm : 0,
33407     labelxs : 0,
33408     
33409     getAutoCreate : function()
33410     {
33411         var cfg = {
33412             tag : 'div',
33413             cls : 'row roo-date-split-field-group',
33414             cn : [
33415                 {
33416                     tag : 'input',
33417                     type : 'hidden',
33418                     cls : 'form-hidden-field roo-date-split-field-group-value',
33419                     name : this.name
33420                 }
33421             ]
33422         };
33423         
33424         var labelCls = 'col-md-12';
33425         var contentCls = 'col-md-4';
33426         
33427         if(this.fieldLabel){
33428             
33429             var label = {
33430                 tag : 'div',
33431                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33432                 cn : [
33433                     {
33434                         tag : 'label',
33435                         html : this.fieldLabel
33436                     }
33437                 ]
33438             };
33439             
33440             if(this.labelAlign == 'left'){
33441             
33442                 if(this.labelWidth > 12){
33443                     label.style = "width: " + this.labelWidth + 'px';
33444                 }
33445
33446                 if(this.labelWidth < 13 && this.labelmd == 0){
33447                     this.labelmd = this.labelWidth;
33448                 }
33449
33450                 if(this.labellg > 0){
33451                     labelCls = ' col-lg-' + this.labellg;
33452                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33453                 }
33454
33455                 if(this.labelmd > 0){
33456                     labelCls = ' col-md-' + this.labelmd;
33457                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33458                 }
33459
33460                 if(this.labelsm > 0){
33461                     labelCls = ' col-sm-' + this.labelsm;
33462                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33463                 }
33464
33465                 if(this.labelxs > 0){
33466                     labelCls = ' col-xs-' + this.labelxs;
33467                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33468                 }
33469             }
33470             
33471             label.cls += ' ' + labelCls;
33472             
33473             cfg.cn.push(label);
33474         }
33475         
33476         Roo.each(['day', 'month', 'year'], function(t){
33477             cfg.cn.push({
33478                 tag : 'div',
33479                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33480             });
33481         }, this);
33482         
33483         return cfg;
33484     },
33485     
33486     inputEl: function ()
33487     {
33488         return this.el.select('.roo-date-split-field-group-value', true).first();
33489     },
33490     
33491     onRender : function(ct, position) 
33492     {
33493         var _this = this;
33494         
33495         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33496         
33497         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33498         
33499         this.dayField = new Roo.bootstrap.ComboBox({
33500             allowBlank : this.dayAllowBlank,
33501             alwaysQuery : true,
33502             displayField : 'value',
33503             editable : false,
33504             fieldLabel : '',
33505             forceSelection : true,
33506             mode : 'local',
33507             placeholder : this.dayPlaceholder,
33508             selectOnFocus : true,
33509             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33510             triggerAction : 'all',
33511             typeAhead : true,
33512             valueField : 'value',
33513             store : new Roo.data.SimpleStore({
33514                 data : (function() {    
33515                     var days = [];
33516                     _this.fireEvent('days', _this, days);
33517                     return days;
33518                 })(),
33519                 fields : [ 'value' ]
33520             }),
33521             listeners : {
33522                 select : function (_self, record, index)
33523                 {
33524                     _this.setValue(_this.getValue());
33525                 }
33526             }
33527         });
33528
33529         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33530         
33531         this.monthField = new Roo.bootstrap.MonthField({
33532             after : '<i class=\"fa fa-calendar\"></i>',
33533             allowBlank : this.monthAllowBlank,
33534             placeholder : this.monthPlaceholder,
33535             readOnly : true,
33536             listeners : {
33537                 render : function (_self)
33538                 {
33539                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33540                         e.preventDefault();
33541                         _self.focus();
33542                     });
33543                 },
33544                 select : function (_self, oldvalue, newvalue)
33545                 {
33546                     _this.setValue(_this.getValue());
33547                 }
33548             }
33549         });
33550         
33551         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33552         
33553         this.yearField = new Roo.bootstrap.ComboBox({
33554             allowBlank : this.yearAllowBlank,
33555             alwaysQuery : true,
33556             displayField : 'value',
33557             editable : false,
33558             fieldLabel : '',
33559             forceSelection : true,
33560             mode : 'local',
33561             placeholder : this.yearPlaceholder,
33562             selectOnFocus : true,
33563             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33564             triggerAction : 'all',
33565             typeAhead : true,
33566             valueField : 'value',
33567             store : new Roo.data.SimpleStore({
33568                 data : (function() {
33569                     var years = [];
33570                     _this.fireEvent('years', _this, years);
33571                     return years;
33572                 })(),
33573                 fields : [ 'value' ]
33574             }),
33575             listeners : {
33576                 select : function (_self, record, index)
33577                 {
33578                     _this.setValue(_this.getValue());
33579                 }
33580             }
33581         });
33582
33583         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33584     },
33585     
33586     setValue : function(v, format)
33587     {
33588         this.inputEl.dom.value = v;
33589         
33590         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33591         
33592         var d = Date.parseDate(v, f);
33593         
33594         if(!d){
33595             this.validate();
33596             return;
33597         }
33598         
33599         this.setDay(d.format(this.dayFormat));
33600         this.setMonth(d.format(this.monthFormat));
33601         this.setYear(d.format(this.yearFormat));
33602         
33603         this.validate();
33604         
33605         return;
33606     },
33607     
33608     setDay : function(v)
33609     {
33610         this.dayField.setValue(v);
33611         this.inputEl.dom.value = this.getValue();
33612         this.validate();
33613         return;
33614     },
33615     
33616     setMonth : function(v)
33617     {
33618         this.monthField.setValue(v, true);
33619         this.inputEl.dom.value = this.getValue();
33620         this.validate();
33621         return;
33622     },
33623     
33624     setYear : function(v)
33625     {
33626         this.yearField.setValue(v);
33627         this.inputEl.dom.value = this.getValue();
33628         this.validate();
33629         return;
33630     },
33631     
33632     getDay : function()
33633     {
33634         return this.dayField.getValue();
33635     },
33636     
33637     getMonth : function()
33638     {
33639         return this.monthField.getValue();
33640     },
33641     
33642     getYear : function()
33643     {
33644         return this.yearField.getValue();
33645     },
33646     
33647     getValue : function()
33648     {
33649         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33650         
33651         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33652         
33653         return date;
33654     },
33655     
33656     reset : function()
33657     {
33658         this.setDay('');
33659         this.setMonth('');
33660         this.setYear('');
33661         this.inputEl.dom.value = '';
33662         this.validate();
33663         return;
33664     },
33665     
33666     validate : function()
33667     {
33668         var d = this.dayField.validate();
33669         var m = this.monthField.validate();
33670         var y = this.yearField.validate();
33671         
33672         var valid = true;
33673         
33674         if(
33675                 (!this.dayAllowBlank && !d) ||
33676                 (!this.monthAllowBlank && !m) ||
33677                 (!this.yearAllowBlank && !y)
33678         ){
33679             valid = false;
33680         }
33681         
33682         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33683             return valid;
33684         }
33685         
33686         if(valid){
33687             this.markValid();
33688             return valid;
33689         }
33690         
33691         this.markInvalid();
33692         
33693         return valid;
33694     },
33695     
33696     markValid : function()
33697     {
33698         
33699         var label = this.el.select('label', true).first();
33700         var icon = this.el.select('i.fa-star', true).first();
33701
33702         if(label && icon){
33703             icon.remove();
33704         }
33705         
33706         this.fireEvent('valid', this);
33707     },
33708     
33709      /**
33710      * Mark this field as invalid
33711      * @param {String} msg The validation message
33712      */
33713     markInvalid : function(msg)
33714     {
33715         
33716         var label = this.el.select('label', true).first();
33717         var icon = this.el.select('i.fa-star', true).first();
33718
33719         if(label && !icon){
33720             this.el.select('.roo-date-split-field-label', true).createChild({
33721                 tag : 'i',
33722                 cls : 'text-danger fa fa-lg fa-star',
33723                 tooltip : 'This field is required',
33724                 style : 'margin-right:5px;'
33725             }, label, true);
33726         }
33727         
33728         this.fireEvent('invalid', this, msg);
33729     },
33730     
33731     clearInvalid : function()
33732     {
33733         var label = this.el.select('label', true).first();
33734         var icon = this.el.select('i.fa-star', true).first();
33735
33736         if(label && icon){
33737             icon.remove();
33738         }
33739         
33740         this.fireEvent('valid', this);
33741     },
33742     
33743     getName: function()
33744     {
33745         return this.name;
33746     }
33747     
33748 });
33749
33750  /**
33751  *
33752  * This is based on 
33753  * http://masonry.desandro.com
33754  *
33755  * The idea is to render all the bricks based on vertical width...
33756  *
33757  * The original code extends 'outlayer' - we might need to use that....
33758  * 
33759  */
33760
33761
33762 /**
33763  * @class Roo.bootstrap.LayoutMasonry
33764  * @extends Roo.bootstrap.Component
33765  * Bootstrap Layout Masonry class
33766  * 
33767  * @constructor
33768  * Create a new Element
33769  * @param {Object} config The config object
33770  */
33771
33772 Roo.bootstrap.LayoutMasonry = function(config){
33773     
33774     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33775     
33776     this.bricks = [];
33777     
33778     Roo.bootstrap.LayoutMasonry.register(this);
33779     
33780     this.addEvents({
33781         // raw events
33782         /**
33783          * @event layout
33784          * Fire after layout the items
33785          * @param {Roo.bootstrap.LayoutMasonry} this
33786          * @param {Roo.EventObject} e
33787          */
33788         "layout" : true
33789     });
33790     
33791 };
33792
33793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33794     
33795     /**
33796      * @cfg {Boolean} isLayoutInstant = no animation?
33797      */   
33798     isLayoutInstant : false, // needed?
33799    
33800     /**
33801      * @cfg {Number} boxWidth  width of the columns
33802      */   
33803     boxWidth : 450,
33804     
33805       /**
33806      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33807      */   
33808     boxHeight : 0,
33809     
33810     /**
33811      * @cfg {Number} padWidth padding below box..
33812      */   
33813     padWidth : 10, 
33814     
33815     /**
33816      * @cfg {Number} gutter gutter width..
33817      */   
33818     gutter : 10,
33819     
33820      /**
33821      * @cfg {Number} maxCols maximum number of columns
33822      */   
33823     
33824     maxCols: 0,
33825     
33826     /**
33827      * @cfg {Boolean} isAutoInitial defalut true
33828      */   
33829     isAutoInitial : true, 
33830     
33831     containerWidth: 0,
33832     
33833     /**
33834      * @cfg {Boolean} isHorizontal defalut false
33835      */   
33836     isHorizontal : false, 
33837
33838     currentSize : null,
33839     
33840     tag: 'div',
33841     
33842     cls: '',
33843     
33844     bricks: null, //CompositeElement
33845     
33846     cols : 1,
33847     
33848     _isLayoutInited : false,
33849     
33850 //    isAlternative : false, // only use for vertical layout...
33851     
33852     /**
33853      * @cfg {Number} alternativePadWidth padding below box..
33854      */   
33855     alternativePadWidth : 50,
33856     
33857     selectedBrick : [],
33858     
33859     getAutoCreate : function(){
33860         
33861         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33862         
33863         var cfg = {
33864             tag: this.tag,
33865             cls: 'blog-masonary-wrapper ' + this.cls,
33866             cn : {
33867                 cls : 'mas-boxes masonary'
33868             }
33869         };
33870         
33871         return cfg;
33872     },
33873     
33874     getChildContainer: function( )
33875     {
33876         if (this.boxesEl) {
33877             return this.boxesEl;
33878         }
33879         
33880         this.boxesEl = this.el.select('.mas-boxes').first();
33881         
33882         return this.boxesEl;
33883     },
33884     
33885     
33886     initEvents : function()
33887     {
33888         var _this = this;
33889         
33890         if(this.isAutoInitial){
33891             Roo.log('hook children rendered');
33892             this.on('childrenrendered', function() {
33893                 Roo.log('children rendered');
33894                 _this.initial();
33895             } ,this);
33896         }
33897     },
33898     
33899     initial : function()
33900     {
33901         this.selectedBrick = [];
33902         
33903         this.currentSize = this.el.getBox(true);
33904         
33905         Roo.EventManager.onWindowResize(this.resize, this); 
33906
33907         if(!this.isAutoInitial){
33908             this.layout();
33909             return;
33910         }
33911         
33912         this.layout();
33913         
33914         return;
33915         //this.layout.defer(500,this);
33916         
33917     },
33918     
33919     resize : function()
33920     {
33921         var cs = this.el.getBox(true);
33922         
33923         if (
33924                 this.currentSize.width == cs.width && 
33925                 this.currentSize.x == cs.x && 
33926                 this.currentSize.height == cs.height && 
33927                 this.currentSize.y == cs.y 
33928         ) {
33929             Roo.log("no change in with or X or Y");
33930             return;
33931         }
33932         
33933         this.currentSize = cs;
33934         
33935         this.layout();
33936         
33937     },
33938     
33939     layout : function()
33940     {   
33941         this._resetLayout();
33942         
33943         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33944         
33945         this.layoutItems( isInstant );
33946       
33947         this._isLayoutInited = true;
33948         
33949         this.fireEvent('layout', this);
33950         
33951     },
33952     
33953     _resetLayout : function()
33954     {
33955         if(this.isHorizontal){
33956             this.horizontalMeasureColumns();
33957             return;
33958         }
33959         
33960         this.verticalMeasureColumns();
33961         
33962     },
33963     
33964     verticalMeasureColumns : function()
33965     {
33966         this.getContainerWidth();
33967         
33968 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33969 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33970 //            return;
33971 //        }
33972         
33973         var boxWidth = this.boxWidth + this.padWidth;
33974         
33975         if(this.containerWidth < this.boxWidth){
33976             boxWidth = this.containerWidth
33977         }
33978         
33979         var containerWidth = this.containerWidth;
33980         
33981         var cols = Math.floor(containerWidth / boxWidth);
33982         
33983         this.cols = Math.max( cols, 1 );
33984         
33985         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33986         
33987         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33988         
33989         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33990         
33991         this.colWidth = boxWidth + avail - this.padWidth;
33992         
33993         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33994         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33995     },
33996     
33997     horizontalMeasureColumns : function()
33998     {
33999         this.getContainerWidth();
34000         
34001         var boxWidth = this.boxWidth;
34002         
34003         if(this.containerWidth < boxWidth){
34004             boxWidth = this.containerWidth;
34005         }
34006         
34007         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34008         
34009         this.el.setHeight(boxWidth);
34010         
34011     },
34012     
34013     getContainerWidth : function()
34014     {
34015         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34016     },
34017     
34018     layoutItems : function( isInstant )
34019     {
34020         Roo.log(this.bricks);
34021         
34022         var items = Roo.apply([], this.bricks);
34023         
34024         if(this.isHorizontal){
34025             this._horizontalLayoutItems( items , isInstant );
34026             return;
34027         }
34028         
34029 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34030 //            this._verticalAlternativeLayoutItems( items , isInstant );
34031 //            return;
34032 //        }
34033         
34034         this._verticalLayoutItems( items , isInstant );
34035         
34036     },
34037     
34038     _verticalLayoutItems : function ( items , isInstant)
34039     {
34040         if ( !items || !items.length ) {
34041             return;
34042         }
34043         
34044         var standard = [
34045             ['xs', 'xs', 'xs', 'tall'],
34046             ['xs', 'xs', 'tall'],
34047             ['xs', 'xs', 'sm'],
34048             ['xs', 'xs', 'xs'],
34049             ['xs', 'tall'],
34050             ['xs', 'sm'],
34051             ['xs', 'xs'],
34052             ['xs'],
34053             
34054             ['sm', 'xs', 'xs'],
34055             ['sm', 'xs'],
34056             ['sm'],
34057             
34058             ['tall', 'xs', 'xs', 'xs'],
34059             ['tall', 'xs', 'xs'],
34060             ['tall', 'xs'],
34061             ['tall']
34062             
34063         ];
34064         
34065         var queue = [];
34066         
34067         var boxes = [];
34068         
34069         var box = [];
34070         
34071         Roo.each(items, function(item, k){
34072             
34073             switch (item.size) {
34074                 // these layouts take up a full box,
34075                 case 'md' :
34076                 case 'md-left' :
34077                 case 'md-right' :
34078                 case 'wide' :
34079                     
34080                     if(box.length){
34081                         boxes.push(box);
34082                         box = [];
34083                     }
34084                     
34085                     boxes.push([item]);
34086                     
34087                     break;
34088                     
34089                 case 'xs' :
34090                 case 'sm' :
34091                 case 'tall' :
34092                     
34093                     box.push(item);
34094                     
34095                     break;
34096                 default :
34097                     break;
34098                     
34099             }
34100             
34101         }, this);
34102         
34103         if(box.length){
34104             boxes.push(box);
34105             box = [];
34106         }
34107         
34108         var filterPattern = function(box, length)
34109         {
34110             if(!box.length){
34111                 return;
34112             }
34113             
34114             var match = false;
34115             
34116             var pattern = box.slice(0, length);
34117             
34118             var format = [];
34119             
34120             Roo.each(pattern, function(i){
34121                 format.push(i.size);
34122             }, this);
34123             
34124             Roo.each(standard, function(s){
34125                 
34126                 if(String(s) != String(format)){
34127                     return;
34128                 }
34129                 
34130                 match = true;
34131                 return false;
34132                 
34133             }, this);
34134             
34135             if(!match && length == 1){
34136                 return;
34137             }
34138             
34139             if(!match){
34140                 filterPattern(box, length - 1);
34141                 return;
34142             }
34143                 
34144             queue.push(pattern);
34145
34146             box = box.slice(length, box.length);
34147
34148             filterPattern(box, 4);
34149
34150             return;
34151             
34152         }
34153         
34154         Roo.each(boxes, function(box, k){
34155             
34156             if(!box.length){
34157                 return;
34158             }
34159             
34160             if(box.length == 1){
34161                 queue.push(box);
34162                 return;
34163             }
34164             
34165             filterPattern(box, 4);
34166             
34167         }, this);
34168         
34169         this._processVerticalLayoutQueue( queue, isInstant );
34170         
34171     },
34172     
34173 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34174 //    {
34175 //        if ( !items || !items.length ) {
34176 //            return;
34177 //        }
34178 //
34179 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34180 //        
34181 //    },
34182     
34183     _horizontalLayoutItems : function ( items , isInstant)
34184     {
34185         if ( !items || !items.length || items.length < 3) {
34186             return;
34187         }
34188         
34189         items.reverse();
34190         
34191         var eItems = items.slice(0, 3);
34192         
34193         items = items.slice(3, items.length);
34194         
34195         var standard = [
34196             ['xs', 'xs', 'xs', 'wide'],
34197             ['xs', 'xs', 'wide'],
34198             ['xs', 'xs', 'sm'],
34199             ['xs', 'xs', 'xs'],
34200             ['xs', 'wide'],
34201             ['xs', 'sm'],
34202             ['xs', 'xs'],
34203             ['xs'],
34204             
34205             ['sm', 'xs', 'xs'],
34206             ['sm', 'xs'],
34207             ['sm'],
34208             
34209             ['wide', 'xs', 'xs', 'xs'],
34210             ['wide', 'xs', 'xs'],
34211             ['wide', 'xs'],
34212             ['wide'],
34213             
34214             ['wide-thin']
34215         ];
34216         
34217         var queue = [];
34218         
34219         var boxes = [];
34220         
34221         var box = [];
34222         
34223         Roo.each(items, function(item, k){
34224             
34225             switch (item.size) {
34226                 case 'md' :
34227                 case 'md-left' :
34228                 case 'md-right' :
34229                 case 'tall' :
34230                     
34231                     if(box.length){
34232                         boxes.push(box);
34233                         box = [];
34234                     }
34235                     
34236                     boxes.push([item]);
34237                     
34238                     break;
34239                     
34240                 case 'xs' :
34241                 case 'sm' :
34242                 case 'wide' :
34243                 case 'wide-thin' :
34244                     
34245                     box.push(item);
34246                     
34247                     break;
34248                 default :
34249                     break;
34250                     
34251             }
34252             
34253         }, this);
34254         
34255         if(box.length){
34256             boxes.push(box);
34257             box = [];
34258         }
34259         
34260         var filterPattern = function(box, length)
34261         {
34262             if(!box.length){
34263                 return;
34264             }
34265             
34266             var match = false;
34267             
34268             var pattern = box.slice(0, length);
34269             
34270             var format = [];
34271             
34272             Roo.each(pattern, function(i){
34273                 format.push(i.size);
34274             }, this);
34275             
34276             Roo.each(standard, function(s){
34277                 
34278                 if(String(s) != String(format)){
34279                     return;
34280                 }
34281                 
34282                 match = true;
34283                 return false;
34284                 
34285             }, this);
34286             
34287             if(!match && length == 1){
34288                 return;
34289             }
34290             
34291             if(!match){
34292                 filterPattern(box, length - 1);
34293                 return;
34294             }
34295                 
34296             queue.push(pattern);
34297
34298             box = box.slice(length, box.length);
34299
34300             filterPattern(box, 4);
34301
34302             return;
34303             
34304         }
34305         
34306         Roo.each(boxes, function(box, k){
34307             
34308             if(!box.length){
34309                 return;
34310             }
34311             
34312             if(box.length == 1){
34313                 queue.push(box);
34314                 return;
34315             }
34316             
34317             filterPattern(box, 4);
34318             
34319         }, this);
34320         
34321         
34322         var prune = [];
34323         
34324         var pos = this.el.getBox(true);
34325         
34326         var minX = pos.x;
34327         
34328         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34329         
34330         var hit_end = false;
34331         
34332         Roo.each(queue, function(box){
34333             
34334             if(hit_end){
34335                 
34336                 Roo.each(box, function(b){
34337                 
34338                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34339                     b.el.hide();
34340
34341                 }, this);
34342
34343                 return;
34344             }
34345             
34346             var mx = 0;
34347             
34348             Roo.each(box, function(b){
34349                 
34350                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34351                 b.el.show();
34352
34353                 mx = Math.max(mx, b.x);
34354                 
34355             }, this);
34356             
34357             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34358             
34359             if(maxX < minX){
34360                 
34361                 Roo.each(box, function(b){
34362                 
34363                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34364                     b.el.hide();
34365                     
34366                 }, this);
34367                 
34368                 hit_end = true;
34369                 
34370                 return;
34371             }
34372             
34373             prune.push(box);
34374             
34375         }, this);
34376         
34377         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34378     },
34379     
34380     /** Sets position of item in DOM
34381     * @param {Element} item
34382     * @param {Number} x - horizontal position
34383     * @param {Number} y - vertical position
34384     * @param {Boolean} isInstant - disables transitions
34385     */
34386     _processVerticalLayoutQueue : function( queue, isInstant )
34387     {
34388         var pos = this.el.getBox(true);
34389         var x = pos.x;
34390         var y = pos.y;
34391         var maxY = [];
34392         
34393         for (var i = 0; i < this.cols; i++){
34394             maxY[i] = pos.y;
34395         }
34396         
34397         Roo.each(queue, function(box, k){
34398             
34399             var col = k % this.cols;
34400             
34401             Roo.each(box, function(b,kk){
34402                 
34403                 b.el.position('absolute');
34404                 
34405                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34406                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34407                 
34408                 if(b.size == 'md-left' || b.size == 'md-right'){
34409                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34410                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34411                 }
34412                 
34413                 b.el.setWidth(width);
34414                 b.el.setHeight(height);
34415                 // iframe?
34416                 b.el.select('iframe',true).setSize(width,height);
34417                 
34418             }, this);
34419             
34420             for (var i = 0; i < this.cols; i++){
34421                 
34422                 if(maxY[i] < maxY[col]){
34423                     col = i;
34424                     continue;
34425                 }
34426                 
34427                 col = Math.min(col, i);
34428                 
34429             }
34430             
34431             x = pos.x + col * (this.colWidth + this.padWidth);
34432             
34433             y = maxY[col];
34434             
34435             var positions = [];
34436             
34437             switch (box.length){
34438                 case 1 :
34439                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34440                     break;
34441                 case 2 :
34442                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34443                     break;
34444                 case 3 :
34445                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34446                     break;
34447                 case 4 :
34448                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34449                     break;
34450                 default :
34451                     break;
34452             }
34453             
34454             Roo.each(box, function(b,kk){
34455                 
34456                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34457                 
34458                 var sz = b.el.getSize();
34459                 
34460                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34461                 
34462             }, this);
34463             
34464         }, this);
34465         
34466         var mY = 0;
34467         
34468         for (var i = 0; i < this.cols; i++){
34469             mY = Math.max(mY, maxY[i]);
34470         }
34471         
34472         this.el.setHeight(mY - pos.y);
34473         
34474     },
34475     
34476 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34477 //    {
34478 //        var pos = this.el.getBox(true);
34479 //        var x = pos.x;
34480 //        var y = pos.y;
34481 //        var maxX = pos.right;
34482 //        
34483 //        var maxHeight = 0;
34484 //        
34485 //        Roo.each(items, function(item, k){
34486 //            
34487 //            var c = k % 2;
34488 //            
34489 //            item.el.position('absolute');
34490 //                
34491 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34492 //
34493 //            item.el.setWidth(width);
34494 //
34495 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34496 //
34497 //            item.el.setHeight(height);
34498 //            
34499 //            if(c == 0){
34500 //                item.el.setXY([x, y], isInstant ? false : true);
34501 //            } else {
34502 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34503 //            }
34504 //            
34505 //            y = y + height + this.alternativePadWidth;
34506 //            
34507 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34508 //            
34509 //        }, this);
34510 //        
34511 //        this.el.setHeight(maxHeight);
34512 //        
34513 //    },
34514     
34515     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34516     {
34517         var pos = this.el.getBox(true);
34518         
34519         var minX = pos.x;
34520         var minY = pos.y;
34521         
34522         var maxX = pos.right;
34523         
34524         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34525         
34526         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34527         
34528         Roo.each(queue, function(box, k){
34529             
34530             Roo.each(box, function(b, kk){
34531                 
34532                 b.el.position('absolute');
34533                 
34534                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34535                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34536                 
34537                 if(b.size == 'md-left' || b.size == 'md-right'){
34538                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34539                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34540                 }
34541                 
34542                 b.el.setWidth(width);
34543                 b.el.setHeight(height);
34544                 
34545             }, this);
34546             
34547             if(!box.length){
34548                 return;
34549             }
34550             
34551             var positions = [];
34552             
34553             switch (box.length){
34554                 case 1 :
34555                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34556                     break;
34557                 case 2 :
34558                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34559                     break;
34560                 case 3 :
34561                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34562                     break;
34563                 case 4 :
34564                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34565                     break;
34566                 default :
34567                     break;
34568             }
34569             
34570             Roo.each(box, function(b,kk){
34571                 
34572                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34573                 
34574                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34575                 
34576             }, this);
34577             
34578         }, this);
34579         
34580     },
34581     
34582     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34583     {
34584         Roo.each(eItems, function(b,k){
34585             
34586             b.size = (k == 0) ? 'sm' : 'xs';
34587             b.x = (k == 0) ? 2 : 1;
34588             b.y = (k == 0) ? 2 : 1;
34589             
34590             b.el.position('absolute');
34591             
34592             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34593                 
34594             b.el.setWidth(width);
34595             
34596             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34597             
34598             b.el.setHeight(height);
34599             
34600         }, this);
34601
34602         var positions = [];
34603         
34604         positions.push({
34605             x : maxX - this.unitWidth * 2 - this.gutter,
34606             y : minY
34607         });
34608         
34609         positions.push({
34610             x : maxX - this.unitWidth,
34611             y : minY + (this.unitWidth + this.gutter) * 2
34612         });
34613         
34614         positions.push({
34615             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34616             y : minY
34617         });
34618         
34619         Roo.each(eItems, function(b,k){
34620             
34621             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34622
34623         }, this);
34624         
34625     },
34626     
34627     getVerticalOneBoxColPositions : function(x, y, box)
34628     {
34629         var pos = [];
34630         
34631         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34632         
34633         if(box[0].size == 'md-left'){
34634             rand = 0;
34635         }
34636         
34637         if(box[0].size == 'md-right'){
34638             rand = 1;
34639         }
34640         
34641         pos.push({
34642             x : x + (this.unitWidth + this.gutter) * rand,
34643             y : y
34644         });
34645         
34646         return pos;
34647     },
34648     
34649     getVerticalTwoBoxColPositions : function(x, y, box)
34650     {
34651         var pos = [];
34652         
34653         if(box[0].size == 'xs'){
34654             
34655             pos.push({
34656                 x : x,
34657                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34658             });
34659
34660             pos.push({
34661                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34662                 y : y
34663             });
34664             
34665             return pos;
34666             
34667         }
34668         
34669         pos.push({
34670             x : x,
34671             y : y
34672         });
34673
34674         pos.push({
34675             x : x + (this.unitWidth + this.gutter) * 2,
34676             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34677         });
34678         
34679         return pos;
34680         
34681     },
34682     
34683     getVerticalThreeBoxColPositions : function(x, y, box)
34684     {
34685         var pos = [];
34686         
34687         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34688             
34689             pos.push({
34690                 x : x,
34691                 y : y
34692             });
34693
34694             pos.push({
34695                 x : x + (this.unitWidth + this.gutter) * 1,
34696                 y : y
34697             });
34698             
34699             pos.push({
34700                 x : x + (this.unitWidth + this.gutter) * 2,
34701                 y : y
34702             });
34703             
34704             return pos;
34705             
34706         }
34707         
34708         if(box[0].size == 'xs' && box[1].size == 'xs'){
34709             
34710             pos.push({
34711                 x : x,
34712                 y : y
34713             });
34714
34715             pos.push({
34716                 x : x,
34717                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34718             });
34719             
34720             pos.push({
34721                 x : x + (this.unitWidth + this.gutter) * 1,
34722                 y : y
34723             });
34724             
34725             return pos;
34726             
34727         }
34728         
34729         pos.push({
34730             x : x,
34731             y : y
34732         });
34733
34734         pos.push({
34735             x : x + (this.unitWidth + this.gutter) * 2,
34736             y : y
34737         });
34738
34739         pos.push({
34740             x : x + (this.unitWidth + this.gutter) * 2,
34741             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34742         });
34743             
34744         return pos;
34745         
34746     },
34747     
34748     getVerticalFourBoxColPositions : function(x, y, box)
34749     {
34750         var pos = [];
34751         
34752         if(box[0].size == 'xs'){
34753             
34754             pos.push({
34755                 x : x,
34756                 y : y
34757             });
34758
34759             pos.push({
34760                 x : x,
34761                 y : y + (this.unitHeight + this.gutter) * 1
34762             });
34763             
34764             pos.push({
34765                 x : x,
34766                 y : y + (this.unitHeight + this.gutter) * 2
34767             });
34768             
34769             pos.push({
34770                 x : x + (this.unitWidth + this.gutter) * 1,
34771                 y : y
34772             });
34773             
34774             return pos;
34775             
34776         }
34777         
34778         pos.push({
34779             x : x,
34780             y : y
34781         });
34782
34783         pos.push({
34784             x : x + (this.unitWidth + this.gutter) * 2,
34785             y : y
34786         });
34787
34788         pos.push({
34789             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34790             y : y + (this.unitHeight + this.gutter) * 1
34791         });
34792
34793         pos.push({
34794             x : x + (this.unitWidth + this.gutter) * 2,
34795             y : y + (this.unitWidth + this.gutter) * 2
34796         });
34797
34798         return pos;
34799         
34800     },
34801     
34802     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34803     {
34804         var pos = [];
34805         
34806         if(box[0].size == 'md-left'){
34807             pos.push({
34808                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34809                 y : minY
34810             });
34811             
34812             return pos;
34813         }
34814         
34815         if(box[0].size == 'md-right'){
34816             pos.push({
34817                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34818                 y : minY + (this.unitWidth + this.gutter) * 1
34819             });
34820             
34821             return pos;
34822         }
34823         
34824         var rand = Math.floor(Math.random() * (4 - box[0].y));
34825         
34826         pos.push({
34827             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34828             y : minY + (this.unitWidth + this.gutter) * rand
34829         });
34830         
34831         return pos;
34832         
34833     },
34834     
34835     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34836     {
34837         var pos = [];
34838         
34839         if(box[0].size == 'xs'){
34840             
34841             pos.push({
34842                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34843                 y : minY
34844             });
34845
34846             pos.push({
34847                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34848                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34849             });
34850             
34851             return pos;
34852             
34853         }
34854         
34855         pos.push({
34856             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34857             y : minY
34858         });
34859
34860         pos.push({
34861             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34862             y : minY + (this.unitWidth + this.gutter) * 2
34863         });
34864         
34865         return pos;
34866         
34867     },
34868     
34869     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34870     {
34871         var pos = [];
34872         
34873         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34874             
34875             pos.push({
34876                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34877                 y : minY
34878             });
34879
34880             pos.push({
34881                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34882                 y : minY + (this.unitWidth + this.gutter) * 1
34883             });
34884             
34885             pos.push({
34886                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34887                 y : minY + (this.unitWidth + this.gutter) * 2
34888             });
34889             
34890             return pos;
34891             
34892         }
34893         
34894         if(box[0].size == 'xs' && box[1].size == 'xs'){
34895             
34896             pos.push({
34897                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898                 y : minY
34899             });
34900
34901             pos.push({
34902                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34903                 y : minY
34904             });
34905             
34906             pos.push({
34907                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34908                 y : minY + (this.unitWidth + this.gutter) * 1
34909             });
34910             
34911             return pos;
34912             
34913         }
34914         
34915         pos.push({
34916             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34917             y : minY
34918         });
34919
34920         pos.push({
34921             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34922             y : minY + (this.unitWidth + this.gutter) * 2
34923         });
34924
34925         pos.push({
34926             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34927             y : minY + (this.unitWidth + this.gutter) * 2
34928         });
34929             
34930         return pos;
34931         
34932     },
34933     
34934     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34935     {
34936         var pos = [];
34937         
34938         if(box[0].size == 'xs'){
34939             
34940             pos.push({
34941                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34942                 y : minY
34943             });
34944
34945             pos.push({
34946                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34947                 y : minY
34948             });
34949             
34950             pos.push({
34951                 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),
34952                 y : minY
34953             });
34954             
34955             pos.push({
34956                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34957                 y : minY + (this.unitWidth + this.gutter) * 1
34958             });
34959             
34960             return pos;
34961             
34962         }
34963         
34964         pos.push({
34965             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34966             y : minY
34967         });
34968         
34969         pos.push({
34970             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34971             y : minY + (this.unitWidth + this.gutter) * 2
34972         });
34973         
34974         pos.push({
34975             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34976             y : minY + (this.unitWidth + this.gutter) * 2
34977         });
34978         
34979         pos.push({
34980             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),
34981             y : minY + (this.unitWidth + this.gutter) * 2
34982         });
34983
34984         return pos;
34985         
34986     },
34987     
34988     /**
34989     * remove a Masonry Brick
34990     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34991     */
34992     removeBrick : function(brick_id)
34993     {
34994         if (!brick_id) {
34995             return;
34996         }
34997         
34998         for (var i = 0; i<this.bricks.length; i++) {
34999             if (this.bricks[i].id == brick_id) {
35000                 this.bricks.splice(i,1);
35001                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35002                 this.initial();
35003             }
35004         }
35005     },
35006     
35007     /**
35008     * adds a Masonry Brick
35009     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35010     */
35011     addBrick : function(cfg)
35012     {
35013         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35014         //this.register(cn);
35015         cn.parentId = this.id;
35016         cn.render(this.el);
35017         return cn;
35018     },
35019     
35020     /**
35021     * register a Masonry Brick
35022     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35023     */
35024     
35025     register : function(brick)
35026     {
35027         this.bricks.push(brick);
35028         brick.masonryId = this.id;
35029     },
35030     
35031     /**
35032     * clear all the Masonry Brick
35033     */
35034     clearAll : function()
35035     {
35036         this.bricks = [];
35037         //this.getChildContainer().dom.innerHTML = "";
35038         this.el.dom.innerHTML = '';
35039     },
35040     
35041     getSelected : function()
35042     {
35043         if (!this.selectedBrick) {
35044             return false;
35045         }
35046         
35047         return this.selectedBrick;
35048     }
35049 });
35050
35051 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35052     
35053     groups: {},
35054      /**
35055     * register a Masonry Layout
35056     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35057     */
35058     
35059     register : function(layout)
35060     {
35061         this.groups[layout.id] = layout;
35062     },
35063     /**
35064     * fetch a  Masonry Layout based on the masonry layout ID
35065     * @param {string} the masonry layout to add
35066     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35067     */
35068     
35069     get: function(layout_id) {
35070         if (typeof(this.groups[layout_id]) == 'undefined') {
35071             return false;
35072         }
35073         return this.groups[layout_id] ;
35074     }
35075     
35076     
35077     
35078 });
35079
35080  
35081
35082  /**
35083  *
35084  * This is based on 
35085  * http://masonry.desandro.com
35086  *
35087  * The idea is to render all the bricks based on vertical width...
35088  *
35089  * The original code extends 'outlayer' - we might need to use that....
35090  * 
35091  */
35092
35093
35094 /**
35095  * @class Roo.bootstrap.LayoutMasonryAuto
35096  * @extends Roo.bootstrap.Component
35097  * Bootstrap Layout Masonry class
35098  * 
35099  * @constructor
35100  * Create a new Element
35101  * @param {Object} config The config object
35102  */
35103
35104 Roo.bootstrap.LayoutMasonryAuto = function(config){
35105     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35106 };
35107
35108 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35109     
35110       /**
35111      * @cfg {Boolean} isFitWidth  - resize the width..
35112      */   
35113     isFitWidth : false,  // options..
35114     /**
35115      * @cfg {Boolean} isOriginLeft = left align?
35116      */   
35117     isOriginLeft : true,
35118     /**
35119      * @cfg {Boolean} isOriginTop = top align?
35120      */   
35121     isOriginTop : false,
35122     /**
35123      * @cfg {Boolean} isLayoutInstant = no animation?
35124      */   
35125     isLayoutInstant : false, // needed?
35126     /**
35127      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35128      */   
35129     isResizingContainer : true,
35130     /**
35131      * @cfg {Number} columnWidth  width of the columns 
35132      */   
35133     
35134     columnWidth : 0,
35135     
35136     /**
35137      * @cfg {Number} maxCols maximum number of columns
35138      */   
35139     
35140     maxCols: 0,
35141     /**
35142      * @cfg {Number} padHeight padding below box..
35143      */   
35144     
35145     padHeight : 10, 
35146     
35147     /**
35148      * @cfg {Boolean} isAutoInitial defalut true
35149      */   
35150     
35151     isAutoInitial : true, 
35152     
35153     // private?
35154     gutter : 0,
35155     
35156     containerWidth: 0,
35157     initialColumnWidth : 0,
35158     currentSize : null,
35159     
35160     colYs : null, // array.
35161     maxY : 0,
35162     padWidth: 10,
35163     
35164     
35165     tag: 'div',
35166     cls: '',
35167     bricks: null, //CompositeElement
35168     cols : 0, // array?
35169     // element : null, // wrapped now this.el
35170     _isLayoutInited : null, 
35171     
35172     
35173     getAutoCreate : function(){
35174         
35175         var cfg = {
35176             tag: this.tag,
35177             cls: 'blog-masonary-wrapper ' + this.cls,
35178             cn : {
35179                 cls : 'mas-boxes masonary'
35180             }
35181         };
35182         
35183         return cfg;
35184     },
35185     
35186     getChildContainer: function( )
35187     {
35188         if (this.boxesEl) {
35189             return this.boxesEl;
35190         }
35191         
35192         this.boxesEl = this.el.select('.mas-boxes').first();
35193         
35194         return this.boxesEl;
35195     },
35196     
35197     
35198     initEvents : function()
35199     {
35200         var _this = this;
35201         
35202         if(this.isAutoInitial){
35203             Roo.log('hook children rendered');
35204             this.on('childrenrendered', function() {
35205                 Roo.log('children rendered');
35206                 _this.initial();
35207             } ,this);
35208         }
35209         
35210     },
35211     
35212     initial : function()
35213     {
35214         this.reloadItems();
35215
35216         this.currentSize = this.el.getBox(true);
35217
35218         /// was window resize... - let's see if this works..
35219         Roo.EventManager.onWindowResize(this.resize, this); 
35220
35221         if(!this.isAutoInitial){
35222             this.layout();
35223             return;
35224         }
35225         
35226         this.layout.defer(500,this);
35227     },
35228     
35229     reloadItems: function()
35230     {
35231         this.bricks = this.el.select('.masonry-brick', true);
35232         
35233         this.bricks.each(function(b) {
35234             //Roo.log(b.getSize());
35235             if (!b.attr('originalwidth')) {
35236                 b.attr('originalwidth',  b.getSize().width);
35237             }
35238             
35239         });
35240         
35241         Roo.log(this.bricks.elements.length);
35242     },
35243     
35244     resize : function()
35245     {
35246         Roo.log('resize');
35247         var cs = this.el.getBox(true);
35248         
35249         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35250             Roo.log("no change in with or X");
35251             return;
35252         }
35253         this.currentSize = cs;
35254         this.layout();
35255     },
35256     
35257     layout : function()
35258     {
35259          Roo.log('layout');
35260         this._resetLayout();
35261         //this._manageStamps();
35262       
35263         // don't animate first layout
35264         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35265         this.layoutItems( isInstant );
35266       
35267         // flag for initalized
35268         this._isLayoutInited = true;
35269     },
35270     
35271     layoutItems : function( isInstant )
35272     {
35273         //var items = this._getItemsForLayout( this.items );
35274         // original code supports filtering layout items.. we just ignore it..
35275         
35276         this._layoutItems( this.bricks , isInstant );
35277       
35278         this._postLayout();
35279     },
35280     _layoutItems : function ( items , isInstant)
35281     {
35282        //this.fireEvent( 'layout', this, items );
35283     
35284
35285         if ( !items || !items.elements.length ) {
35286           // no items, emit event with empty array
35287             return;
35288         }
35289
35290         var queue = [];
35291         items.each(function(item) {
35292             Roo.log("layout item");
35293             Roo.log(item);
35294             // get x/y object from method
35295             var position = this._getItemLayoutPosition( item );
35296             // enqueue
35297             position.item = item;
35298             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35299             queue.push( position );
35300         }, this);
35301       
35302         this._processLayoutQueue( queue );
35303     },
35304     /** Sets position of item in DOM
35305     * @param {Element} item
35306     * @param {Number} x - horizontal position
35307     * @param {Number} y - vertical position
35308     * @param {Boolean} isInstant - disables transitions
35309     */
35310     _processLayoutQueue : function( queue )
35311     {
35312         for ( var i=0, len = queue.length; i < len; i++ ) {
35313             var obj = queue[i];
35314             obj.item.position('absolute');
35315             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35316         }
35317     },
35318       
35319     
35320     /**
35321     * Any logic you want to do after each layout,
35322     * i.e. size the container
35323     */
35324     _postLayout : function()
35325     {
35326         this.resizeContainer();
35327     },
35328     
35329     resizeContainer : function()
35330     {
35331         if ( !this.isResizingContainer ) {
35332             return;
35333         }
35334         var size = this._getContainerSize();
35335         if ( size ) {
35336             this.el.setSize(size.width,size.height);
35337             this.boxesEl.setSize(size.width,size.height);
35338         }
35339     },
35340     
35341     
35342     
35343     _resetLayout : function()
35344     {
35345         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35346         this.colWidth = this.el.getWidth();
35347         //this.gutter = this.el.getWidth(); 
35348         
35349         this.measureColumns();
35350
35351         // reset column Y
35352         var i = this.cols;
35353         this.colYs = [];
35354         while (i--) {
35355             this.colYs.push( 0 );
35356         }
35357     
35358         this.maxY = 0;
35359     },
35360
35361     measureColumns : function()
35362     {
35363         this.getContainerWidth();
35364       // if columnWidth is 0, default to outerWidth of first item
35365         if ( !this.columnWidth ) {
35366             var firstItem = this.bricks.first();
35367             Roo.log(firstItem);
35368             this.columnWidth  = this.containerWidth;
35369             if (firstItem && firstItem.attr('originalwidth') ) {
35370                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35371             }
35372             // columnWidth fall back to item of first element
35373             Roo.log("set column width?");
35374                         this.initialColumnWidth = this.columnWidth  ;
35375
35376             // if first elem has no width, default to size of container
35377             
35378         }
35379         
35380         
35381         if (this.initialColumnWidth) {
35382             this.columnWidth = this.initialColumnWidth;
35383         }
35384         
35385         
35386             
35387         // column width is fixed at the top - however if container width get's smaller we should
35388         // reduce it...
35389         
35390         // this bit calcs how man columns..
35391             
35392         var columnWidth = this.columnWidth += this.gutter;
35393       
35394         // calculate columns
35395         var containerWidth = this.containerWidth + this.gutter;
35396         
35397         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35398         // fix rounding errors, typically with gutters
35399         var excess = columnWidth - containerWidth % columnWidth;
35400         
35401         
35402         // if overshoot is less than a pixel, round up, otherwise floor it
35403         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35404         cols = Math[ mathMethod ]( cols );
35405         this.cols = Math.max( cols, 1 );
35406         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35407         
35408          // padding positioning..
35409         var totalColWidth = this.cols * this.columnWidth;
35410         var padavail = this.containerWidth - totalColWidth;
35411         // so for 2 columns - we need 3 'pads'
35412         
35413         var padNeeded = (1+this.cols) * this.padWidth;
35414         
35415         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35416         
35417         this.columnWidth += padExtra
35418         //this.padWidth = Math.floor(padavail /  ( this.cols));
35419         
35420         // adjust colum width so that padding is fixed??
35421         
35422         // we have 3 columns ... total = width * 3
35423         // we have X left over... that should be used by 
35424         
35425         //if (this.expandC) {
35426             
35427         //}
35428         
35429         
35430         
35431     },
35432     
35433     getContainerWidth : function()
35434     {
35435        /* // container is parent if fit width
35436         var container = this.isFitWidth ? this.element.parentNode : this.element;
35437         // check that this.size and size are there
35438         // IE8 triggers resize on body size change, so they might not be
35439         
35440         var size = getSize( container );  //FIXME
35441         this.containerWidth = size && size.innerWidth; //FIXME
35442         */
35443          
35444         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35445         
35446     },
35447     
35448     _getItemLayoutPosition : function( item )  // what is item?
35449     {
35450         // we resize the item to our columnWidth..
35451       
35452         item.setWidth(this.columnWidth);
35453         item.autoBoxAdjust  = false;
35454         
35455         var sz = item.getSize();
35456  
35457         // how many columns does this brick span
35458         var remainder = this.containerWidth % this.columnWidth;
35459         
35460         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35461         // round if off by 1 pixel, otherwise use ceil
35462         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35463         colSpan = Math.min( colSpan, this.cols );
35464         
35465         // normally this should be '1' as we dont' currently allow multi width columns..
35466         
35467         var colGroup = this._getColGroup( colSpan );
35468         // get the minimum Y value from the columns
35469         var minimumY = Math.min.apply( Math, colGroup );
35470         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35471         
35472         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35473          
35474         // position the brick
35475         var position = {
35476             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35477             y: this.currentSize.y + minimumY + this.padHeight
35478         };
35479         
35480         Roo.log(position);
35481         // apply setHeight to necessary columns
35482         var setHeight = minimumY + sz.height + this.padHeight;
35483         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35484         
35485         var setSpan = this.cols + 1 - colGroup.length;
35486         for ( var i = 0; i < setSpan; i++ ) {
35487           this.colYs[ shortColIndex + i ] = setHeight ;
35488         }
35489       
35490         return position;
35491     },
35492     
35493     /**
35494      * @param {Number} colSpan - number of columns the element spans
35495      * @returns {Array} colGroup
35496      */
35497     _getColGroup : function( colSpan )
35498     {
35499         if ( colSpan < 2 ) {
35500           // if brick spans only one column, use all the column Ys
35501           return this.colYs;
35502         }
35503       
35504         var colGroup = [];
35505         // how many different places could this brick fit horizontally
35506         var groupCount = this.cols + 1 - colSpan;
35507         // for each group potential horizontal position
35508         for ( var i = 0; i < groupCount; i++ ) {
35509           // make an array of colY values for that one group
35510           var groupColYs = this.colYs.slice( i, i + colSpan );
35511           // and get the max value of the array
35512           colGroup[i] = Math.max.apply( Math, groupColYs );
35513         }
35514         return colGroup;
35515     },
35516     /*
35517     _manageStamp : function( stamp )
35518     {
35519         var stampSize =  stamp.getSize();
35520         var offset = stamp.getBox();
35521         // get the columns that this stamp affects
35522         var firstX = this.isOriginLeft ? offset.x : offset.right;
35523         var lastX = firstX + stampSize.width;
35524         var firstCol = Math.floor( firstX / this.columnWidth );
35525         firstCol = Math.max( 0, firstCol );
35526         
35527         var lastCol = Math.floor( lastX / this.columnWidth );
35528         // lastCol should not go over if multiple of columnWidth #425
35529         lastCol -= lastX % this.columnWidth ? 0 : 1;
35530         lastCol = Math.min( this.cols - 1, lastCol );
35531         
35532         // set colYs to bottom of the stamp
35533         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35534             stampSize.height;
35535             
35536         for ( var i = firstCol; i <= lastCol; i++ ) {
35537           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35538         }
35539     },
35540     */
35541     
35542     _getContainerSize : function()
35543     {
35544         this.maxY = Math.max.apply( Math, this.colYs );
35545         var size = {
35546             height: this.maxY
35547         };
35548       
35549         if ( this.isFitWidth ) {
35550             size.width = this._getContainerFitWidth();
35551         }
35552       
35553         return size;
35554     },
35555     
35556     _getContainerFitWidth : function()
35557     {
35558         var unusedCols = 0;
35559         // count unused columns
35560         var i = this.cols;
35561         while ( --i ) {
35562           if ( this.colYs[i] !== 0 ) {
35563             break;
35564           }
35565           unusedCols++;
35566         }
35567         // fit container to columns that have been used
35568         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35569     },
35570     
35571     needsResizeLayout : function()
35572     {
35573         var previousWidth = this.containerWidth;
35574         this.getContainerWidth();
35575         return previousWidth !== this.containerWidth;
35576     }
35577  
35578 });
35579
35580  
35581
35582  /*
35583  * - LGPL
35584  *
35585  * element
35586  * 
35587  */
35588
35589 /**
35590  * @class Roo.bootstrap.MasonryBrick
35591  * @extends Roo.bootstrap.Component
35592  * Bootstrap MasonryBrick class
35593  * 
35594  * @constructor
35595  * Create a new MasonryBrick
35596  * @param {Object} config The config object
35597  */
35598
35599 Roo.bootstrap.MasonryBrick = function(config){
35600     
35601     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35602     
35603     Roo.bootstrap.MasonryBrick.register(this);
35604     
35605     this.addEvents({
35606         // raw events
35607         /**
35608          * @event click
35609          * When a MasonryBrick is clcik
35610          * @param {Roo.bootstrap.MasonryBrick} this
35611          * @param {Roo.EventObject} e
35612          */
35613         "click" : true
35614     });
35615 };
35616
35617 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35618     
35619     /**
35620      * @cfg {String} title
35621      */   
35622     title : '',
35623     /**
35624      * @cfg {String} html
35625      */   
35626     html : '',
35627     /**
35628      * @cfg {String} bgimage
35629      */   
35630     bgimage : '',
35631     /**
35632      * @cfg {String} videourl
35633      */   
35634     videourl : '',
35635     /**
35636      * @cfg {String} cls
35637      */   
35638     cls : '',
35639     /**
35640      * @cfg {String} href
35641      */   
35642     href : '',
35643     /**
35644      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35645      */   
35646     size : 'xs',
35647     
35648     /**
35649      * @cfg {String} placetitle (center|bottom)
35650      */   
35651     placetitle : '',
35652     
35653     /**
35654      * @cfg {Boolean} isFitContainer defalut true
35655      */   
35656     isFitContainer : true, 
35657     
35658     /**
35659      * @cfg {Boolean} preventDefault defalut false
35660      */   
35661     preventDefault : false, 
35662     
35663     /**
35664      * @cfg {Boolean} inverse defalut false
35665      */   
35666     maskInverse : false, 
35667     
35668     getAutoCreate : function()
35669     {
35670         if(!this.isFitContainer){
35671             return this.getSplitAutoCreate();
35672         }
35673         
35674         var cls = 'masonry-brick masonry-brick-full';
35675         
35676         if(this.href.length){
35677             cls += ' masonry-brick-link';
35678         }
35679         
35680         if(this.bgimage.length){
35681             cls += ' masonry-brick-image';
35682         }
35683         
35684         if(this.maskInverse){
35685             cls += ' mask-inverse';
35686         }
35687         
35688         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35689             cls += ' enable-mask';
35690         }
35691         
35692         if(this.size){
35693             cls += ' masonry-' + this.size + '-brick';
35694         }
35695         
35696         if(this.placetitle.length){
35697             
35698             switch (this.placetitle) {
35699                 case 'center' :
35700                     cls += ' masonry-center-title';
35701                     break;
35702                 case 'bottom' :
35703                     cls += ' masonry-bottom-title';
35704                     break;
35705                 default:
35706                     break;
35707             }
35708             
35709         } else {
35710             if(!this.html.length && !this.bgimage.length){
35711                 cls += ' masonry-center-title';
35712             }
35713
35714             if(!this.html.length && this.bgimage.length){
35715                 cls += ' masonry-bottom-title';
35716             }
35717         }
35718         
35719         if(this.cls){
35720             cls += ' ' + this.cls;
35721         }
35722         
35723         var cfg = {
35724             tag: (this.href.length) ? 'a' : 'div',
35725             cls: cls,
35726             cn: [
35727                 {
35728                     tag: 'div',
35729                     cls: 'masonry-brick-mask'
35730                 },
35731                 {
35732                     tag: 'div',
35733                     cls: 'masonry-brick-paragraph',
35734                     cn: []
35735                 }
35736             ]
35737         };
35738         
35739         if(this.href.length){
35740             cfg.href = this.href;
35741         }
35742         
35743         var cn = cfg.cn[1].cn;
35744         
35745         if(this.title.length){
35746             cn.push({
35747                 tag: 'h4',
35748                 cls: 'masonry-brick-title',
35749                 html: this.title
35750             });
35751         }
35752         
35753         if(this.html.length){
35754             cn.push({
35755                 tag: 'p',
35756                 cls: 'masonry-brick-text',
35757                 html: this.html
35758             });
35759         }
35760         
35761         if (!this.title.length && !this.html.length) {
35762             cfg.cn[1].cls += ' hide';
35763         }
35764         
35765         if(this.bgimage.length){
35766             cfg.cn.push({
35767                 tag: 'img',
35768                 cls: 'masonry-brick-image-view',
35769                 src: this.bgimage
35770             });
35771         }
35772         
35773         if(this.videourl.length){
35774             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35775             // youtube support only?
35776             cfg.cn.push({
35777                 tag: 'iframe',
35778                 cls: 'masonry-brick-image-view',
35779                 src: vurl,
35780                 frameborder : 0,
35781                 allowfullscreen : true
35782             });
35783         }
35784         
35785         return cfg;
35786         
35787     },
35788     
35789     getSplitAutoCreate : function()
35790     {
35791         var cls = 'masonry-brick masonry-brick-split';
35792         
35793         if(this.href.length){
35794             cls += ' masonry-brick-link';
35795         }
35796         
35797         if(this.bgimage.length){
35798             cls += ' masonry-brick-image';
35799         }
35800         
35801         if(this.size){
35802             cls += ' masonry-' + this.size + '-brick';
35803         }
35804         
35805         switch (this.placetitle) {
35806             case 'center' :
35807                 cls += ' masonry-center-title';
35808                 break;
35809             case 'bottom' :
35810                 cls += ' masonry-bottom-title';
35811                 break;
35812             default:
35813                 if(!this.bgimage.length){
35814                     cls += ' masonry-center-title';
35815                 }
35816
35817                 if(this.bgimage.length){
35818                     cls += ' masonry-bottom-title';
35819                 }
35820                 break;
35821         }
35822         
35823         if(this.cls){
35824             cls += ' ' + this.cls;
35825         }
35826         
35827         var cfg = {
35828             tag: (this.href.length) ? 'a' : 'div',
35829             cls: cls,
35830             cn: [
35831                 {
35832                     tag: 'div',
35833                     cls: 'masonry-brick-split-head',
35834                     cn: [
35835                         {
35836                             tag: 'div',
35837                             cls: 'masonry-brick-paragraph',
35838                             cn: []
35839                         }
35840                     ]
35841                 },
35842                 {
35843                     tag: 'div',
35844                     cls: 'masonry-brick-split-body',
35845                     cn: []
35846                 }
35847             ]
35848         };
35849         
35850         if(this.href.length){
35851             cfg.href = this.href;
35852         }
35853         
35854         if(this.title.length){
35855             cfg.cn[0].cn[0].cn.push({
35856                 tag: 'h4',
35857                 cls: 'masonry-brick-title',
35858                 html: this.title
35859             });
35860         }
35861         
35862         if(this.html.length){
35863             cfg.cn[1].cn.push({
35864                 tag: 'p',
35865                 cls: 'masonry-brick-text',
35866                 html: this.html
35867             });
35868         }
35869
35870         if(this.bgimage.length){
35871             cfg.cn[0].cn.push({
35872                 tag: 'img',
35873                 cls: 'masonry-brick-image-view',
35874                 src: this.bgimage
35875             });
35876         }
35877         
35878         if(this.videourl.length){
35879             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35880             // youtube support only?
35881             cfg.cn[0].cn.cn.push({
35882                 tag: 'iframe',
35883                 cls: 'masonry-brick-image-view',
35884                 src: vurl,
35885                 frameborder : 0,
35886                 allowfullscreen : true
35887             });
35888         }
35889         
35890         return cfg;
35891     },
35892     
35893     initEvents: function() 
35894     {
35895         switch (this.size) {
35896             case 'xs' :
35897                 this.x = 1;
35898                 this.y = 1;
35899                 break;
35900             case 'sm' :
35901                 this.x = 2;
35902                 this.y = 2;
35903                 break;
35904             case 'md' :
35905             case 'md-left' :
35906             case 'md-right' :
35907                 this.x = 3;
35908                 this.y = 3;
35909                 break;
35910             case 'tall' :
35911                 this.x = 2;
35912                 this.y = 3;
35913                 break;
35914             case 'wide' :
35915                 this.x = 3;
35916                 this.y = 2;
35917                 break;
35918             case 'wide-thin' :
35919                 this.x = 3;
35920                 this.y = 1;
35921                 break;
35922                         
35923             default :
35924                 break;
35925         }
35926         
35927         if(Roo.isTouch){
35928             this.el.on('touchstart', this.onTouchStart, this);
35929             this.el.on('touchmove', this.onTouchMove, this);
35930             this.el.on('touchend', this.onTouchEnd, this);
35931             this.el.on('contextmenu', this.onContextMenu, this);
35932         } else {
35933             this.el.on('mouseenter'  ,this.enter, this);
35934             this.el.on('mouseleave', this.leave, this);
35935             this.el.on('click', this.onClick, this);
35936         }
35937         
35938         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35939             this.parent().bricks.push(this);   
35940         }
35941         
35942     },
35943     
35944     onClick: function(e, el)
35945     {
35946         var time = this.endTimer - this.startTimer;
35947         // Roo.log(e.preventDefault());
35948         if(Roo.isTouch){
35949             if(time > 1000){
35950                 e.preventDefault();
35951                 return;
35952             }
35953         }
35954         
35955         if(!this.preventDefault){
35956             return;
35957         }
35958         
35959         e.preventDefault();
35960         
35961         if (this.activeClass != '') {
35962             this.selectBrick();
35963         }
35964         
35965         this.fireEvent('click', this, e);
35966     },
35967     
35968     enter: function(e, el)
35969     {
35970         e.preventDefault();
35971         
35972         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35973             return;
35974         }
35975         
35976         if(this.bgimage.length && this.html.length){
35977             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35978         }
35979     },
35980     
35981     leave: function(e, el)
35982     {
35983         e.preventDefault();
35984         
35985         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35986             return;
35987         }
35988         
35989         if(this.bgimage.length && this.html.length){
35990             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35991         }
35992     },
35993     
35994     onTouchStart: function(e, el)
35995     {
35996 //        e.preventDefault();
35997         
35998         this.touchmoved = false;
35999         
36000         if(!this.isFitContainer){
36001             return;
36002         }
36003         
36004         if(!this.bgimage.length || !this.html.length){
36005             return;
36006         }
36007         
36008         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36009         
36010         this.timer = new Date().getTime();
36011         
36012     },
36013     
36014     onTouchMove: function(e, el)
36015     {
36016         this.touchmoved = true;
36017     },
36018     
36019     onContextMenu : function(e,el)
36020     {
36021         e.preventDefault();
36022         e.stopPropagation();
36023         return false;
36024     },
36025     
36026     onTouchEnd: function(e, el)
36027     {
36028 //        e.preventDefault();
36029         
36030         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36031         
36032             this.leave(e,el);
36033             
36034             return;
36035         }
36036         
36037         if(!this.bgimage.length || !this.html.length){
36038             
36039             if(this.href.length){
36040                 window.location.href = this.href;
36041             }
36042             
36043             return;
36044         }
36045         
36046         if(!this.isFitContainer){
36047             return;
36048         }
36049         
36050         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36051         
36052         window.location.href = this.href;
36053     },
36054     
36055     //selection on single brick only
36056     selectBrick : function() {
36057         
36058         if (!this.parentId) {
36059             return;
36060         }
36061         
36062         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36063         var index = m.selectedBrick.indexOf(this.id);
36064         
36065         if ( index > -1) {
36066             m.selectedBrick.splice(index,1);
36067             this.el.removeClass(this.activeClass);
36068             return;
36069         }
36070         
36071         for(var i = 0; i < m.selectedBrick.length; i++) {
36072             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36073             b.el.removeClass(b.activeClass);
36074         }
36075         
36076         m.selectedBrick = [];
36077         
36078         m.selectedBrick.push(this.id);
36079         this.el.addClass(this.activeClass);
36080         return;
36081     },
36082     
36083     isSelected : function(){
36084         return this.el.hasClass(this.activeClass);
36085         
36086     }
36087 });
36088
36089 Roo.apply(Roo.bootstrap.MasonryBrick, {
36090     
36091     //groups: {},
36092     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36093      /**
36094     * register a Masonry Brick
36095     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36096     */
36097     
36098     register : function(brick)
36099     {
36100         //this.groups[brick.id] = brick;
36101         this.groups.add(brick.id, brick);
36102     },
36103     /**
36104     * fetch a  masonry brick based on the masonry brick ID
36105     * @param {string} the masonry brick to add
36106     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36107     */
36108     
36109     get: function(brick_id) 
36110     {
36111         // if (typeof(this.groups[brick_id]) == 'undefined') {
36112         //     return false;
36113         // }
36114         // return this.groups[brick_id] ;
36115         
36116         if(this.groups.key(brick_id)) {
36117             return this.groups.key(brick_id);
36118         }
36119         
36120         return false;
36121     }
36122     
36123     
36124     
36125 });
36126
36127  /*
36128  * - LGPL
36129  *
36130  * element
36131  * 
36132  */
36133
36134 /**
36135  * @class Roo.bootstrap.Brick
36136  * @extends Roo.bootstrap.Component
36137  * Bootstrap Brick class
36138  * 
36139  * @constructor
36140  * Create a new Brick
36141  * @param {Object} config The config object
36142  */
36143
36144 Roo.bootstrap.Brick = function(config){
36145     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36146     
36147     this.addEvents({
36148         // raw events
36149         /**
36150          * @event click
36151          * When a Brick is click
36152          * @param {Roo.bootstrap.Brick} this
36153          * @param {Roo.EventObject} e
36154          */
36155         "click" : true
36156     });
36157 };
36158
36159 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36160     
36161     /**
36162      * @cfg {String} title
36163      */   
36164     title : '',
36165     /**
36166      * @cfg {String} html
36167      */   
36168     html : '',
36169     /**
36170      * @cfg {String} bgimage
36171      */   
36172     bgimage : '',
36173     /**
36174      * @cfg {String} cls
36175      */   
36176     cls : '',
36177     /**
36178      * @cfg {String} href
36179      */   
36180     href : '',
36181     /**
36182      * @cfg {String} video
36183      */   
36184     video : '',
36185     /**
36186      * @cfg {Boolean} square
36187      */   
36188     square : true,
36189     
36190     getAutoCreate : function()
36191     {
36192         var cls = 'roo-brick';
36193         
36194         if(this.href.length){
36195             cls += ' roo-brick-link';
36196         }
36197         
36198         if(this.bgimage.length){
36199             cls += ' roo-brick-image';
36200         }
36201         
36202         if(!this.html.length && !this.bgimage.length){
36203             cls += ' roo-brick-center-title';
36204         }
36205         
36206         if(!this.html.length && this.bgimage.length){
36207             cls += ' roo-brick-bottom-title';
36208         }
36209         
36210         if(this.cls){
36211             cls += ' ' + this.cls;
36212         }
36213         
36214         var cfg = {
36215             tag: (this.href.length) ? 'a' : 'div',
36216             cls: cls,
36217             cn: [
36218                 {
36219                     tag: 'div',
36220                     cls: 'roo-brick-paragraph',
36221                     cn: []
36222                 }
36223             ]
36224         };
36225         
36226         if(this.href.length){
36227             cfg.href = this.href;
36228         }
36229         
36230         var cn = cfg.cn[0].cn;
36231         
36232         if(this.title.length){
36233             cn.push({
36234                 tag: 'h4',
36235                 cls: 'roo-brick-title',
36236                 html: this.title
36237             });
36238         }
36239         
36240         if(this.html.length){
36241             cn.push({
36242                 tag: 'p',
36243                 cls: 'roo-brick-text',
36244                 html: this.html
36245             });
36246         } else {
36247             cn.cls += ' hide';
36248         }
36249         
36250         if(this.bgimage.length){
36251             cfg.cn.push({
36252                 tag: 'img',
36253                 cls: 'roo-brick-image-view',
36254                 src: this.bgimage
36255             });
36256         }
36257         
36258         return cfg;
36259     },
36260     
36261     initEvents: function() 
36262     {
36263         if(this.title.length || this.html.length){
36264             this.el.on('mouseenter'  ,this.enter, this);
36265             this.el.on('mouseleave', this.leave, this);
36266         }
36267         
36268         Roo.EventManager.onWindowResize(this.resize, this); 
36269         
36270         if(this.bgimage.length){
36271             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36272             this.imageEl.on('load', this.onImageLoad, this);
36273             return;
36274         }
36275         
36276         this.resize();
36277     },
36278     
36279     onImageLoad : function()
36280     {
36281         this.resize();
36282     },
36283     
36284     resize : function()
36285     {
36286         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36287         
36288         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36289         
36290         if(this.bgimage.length){
36291             var image = this.el.select('.roo-brick-image-view', true).first();
36292             
36293             image.setWidth(paragraph.getWidth());
36294             
36295             if(this.square){
36296                 image.setHeight(paragraph.getWidth());
36297             }
36298             
36299             this.el.setHeight(image.getHeight());
36300             paragraph.setHeight(image.getHeight());
36301             
36302         }
36303         
36304     },
36305     
36306     enter: function(e, el)
36307     {
36308         e.preventDefault();
36309         
36310         if(this.bgimage.length){
36311             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36312             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36313         }
36314     },
36315     
36316     leave: function(e, el)
36317     {
36318         e.preventDefault();
36319         
36320         if(this.bgimage.length){
36321             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36322             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36323         }
36324     }
36325     
36326 });
36327
36328  
36329
36330  /*
36331  * - LGPL
36332  *
36333  * Number field 
36334  */
36335
36336 /**
36337  * @class Roo.bootstrap.NumberField
36338  * @extends Roo.bootstrap.Input
36339  * Bootstrap NumberField class
36340  * 
36341  * 
36342  * 
36343  * 
36344  * @constructor
36345  * Create a new NumberField
36346  * @param {Object} config The config object
36347  */
36348
36349 Roo.bootstrap.NumberField = function(config){
36350     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36351 };
36352
36353 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36354     
36355     /**
36356      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36357      */
36358     allowDecimals : true,
36359     /**
36360      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36361      */
36362     decimalSeparator : ".",
36363     /**
36364      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36365      */
36366     decimalPrecision : 2,
36367     /**
36368      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36369      */
36370     allowNegative : true,
36371     
36372     /**
36373      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36374      */
36375     allowZero: true,
36376     /**
36377      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36378      */
36379     minValue : Number.NEGATIVE_INFINITY,
36380     /**
36381      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36382      */
36383     maxValue : Number.MAX_VALUE,
36384     /**
36385      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36386      */
36387     minText : "The minimum value for this field is {0}",
36388     /**
36389      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36390      */
36391     maxText : "The maximum value for this field is {0}",
36392     /**
36393      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36394      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36395      */
36396     nanText : "{0} is not a valid number",
36397     /**
36398      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36399      */
36400     thousandsDelimiter : false,
36401     /**
36402      * @cfg {String} valueAlign alignment of value
36403      */
36404     valueAlign : "left",
36405
36406     getAutoCreate : function()
36407     {
36408         var hiddenInput = {
36409             tag: 'input',
36410             type: 'hidden',
36411             id: Roo.id(),
36412             cls: 'hidden-number-input'
36413         };
36414         
36415         if (this.name) {
36416             hiddenInput.name = this.name;
36417         }
36418         
36419         this.name = '';
36420         
36421         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36422         
36423         this.name = hiddenInput.name;
36424         
36425         if(cfg.cn.length > 0) {
36426             cfg.cn.push(hiddenInput);
36427         }
36428         
36429         return cfg;
36430     },
36431
36432     // private
36433     initEvents : function()
36434     {   
36435         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36436         
36437         var allowed = "0123456789";
36438         
36439         if(this.allowDecimals){
36440             allowed += this.decimalSeparator;
36441         }
36442         
36443         if(this.allowNegative){
36444             allowed += "-";
36445         }
36446         
36447         if(this.thousandsDelimiter) {
36448             allowed += ",";
36449         }
36450         
36451         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36452         
36453         var keyPress = function(e){
36454             
36455             var k = e.getKey();
36456             
36457             var c = e.getCharCode();
36458             
36459             if(
36460                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36461                     allowed.indexOf(String.fromCharCode(c)) === -1
36462             ){
36463                 e.stopEvent();
36464                 return;
36465             }
36466             
36467             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36468                 return;
36469             }
36470             
36471             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36472                 e.stopEvent();
36473             }
36474         };
36475         
36476         this.el.on("keypress", keyPress, this);
36477     },
36478     
36479     validateValue : function(value)
36480     {
36481         
36482         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36483             return false;
36484         }
36485         
36486         var num = this.parseValue(value);
36487         
36488         if(isNaN(num)){
36489             this.markInvalid(String.format(this.nanText, value));
36490             return false;
36491         }
36492         
36493         if(num < this.minValue){
36494             this.markInvalid(String.format(this.minText, this.minValue));
36495             return false;
36496         }
36497         
36498         if(num > this.maxValue){
36499             this.markInvalid(String.format(this.maxText, this.maxValue));
36500             return false;
36501         }
36502         
36503         return true;
36504     },
36505
36506     getValue : function()
36507     {
36508         var v = this.hiddenEl().getValue();
36509         
36510         return this.fixPrecision(this.parseValue(v));
36511     },
36512
36513     parseValue : function(value)
36514     {
36515         if(this.thousandsDelimiter) {
36516             value += "";
36517             r = new RegExp(",", "g");
36518             value = value.replace(r, "");
36519         }
36520         
36521         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36522         return isNaN(value) ? '' : value;
36523     },
36524
36525     fixPrecision : function(value)
36526     {
36527         if(this.thousandsDelimiter) {
36528             value += "";
36529             r = new RegExp(",", "g");
36530             value = value.replace(r, "");
36531         }
36532         
36533         var nan = isNaN(value);
36534         
36535         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36536             return nan ? '' : value;
36537         }
36538         return parseFloat(value).toFixed(this.decimalPrecision);
36539     },
36540
36541     setValue : function(v)
36542     {
36543         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36544         
36545         this.value = v;
36546         
36547         if(this.rendered){
36548             
36549             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36550             
36551             this.inputEl().dom.value = (v == '') ? '' :
36552                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36553             
36554             if(!this.allowZero && v === '0') {
36555                 this.hiddenEl().dom.value = '';
36556                 this.inputEl().dom.value = '';
36557             }
36558             
36559             this.validate();
36560         }
36561     },
36562
36563     decimalPrecisionFcn : function(v)
36564     {
36565         return Math.floor(v);
36566     },
36567
36568     beforeBlur : function()
36569     {
36570         var v = this.parseValue(this.getRawValue());
36571         
36572         if(v || v === 0 || v === ''){
36573             this.setValue(v);
36574         }
36575     },
36576     
36577     hiddenEl : function()
36578     {
36579         return this.el.select('input.hidden-number-input',true).first();
36580     }
36581     
36582 });
36583
36584  
36585
36586 /*
36587 * Licence: LGPL
36588 */
36589
36590 /**
36591  * @class Roo.bootstrap.DocumentSlider
36592  * @extends Roo.bootstrap.Component
36593  * Bootstrap DocumentSlider class
36594  * 
36595  * @constructor
36596  * Create a new DocumentViewer
36597  * @param {Object} config The config object
36598  */
36599
36600 Roo.bootstrap.DocumentSlider = function(config){
36601     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36602     
36603     this.files = [];
36604     
36605     this.addEvents({
36606         /**
36607          * @event initial
36608          * Fire after initEvent
36609          * @param {Roo.bootstrap.DocumentSlider} this
36610          */
36611         "initial" : true,
36612         /**
36613          * @event update
36614          * Fire after update
36615          * @param {Roo.bootstrap.DocumentSlider} this
36616          */
36617         "update" : true,
36618         /**
36619          * @event click
36620          * Fire after click
36621          * @param {Roo.bootstrap.DocumentSlider} this
36622          */
36623         "click" : true
36624     });
36625 };
36626
36627 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36628     
36629     files : false,
36630     
36631     indicator : 0,
36632     
36633     getAutoCreate : function()
36634     {
36635         var cfg = {
36636             tag : 'div',
36637             cls : 'roo-document-slider',
36638             cn : [
36639                 {
36640                     tag : 'div',
36641                     cls : 'roo-document-slider-header',
36642                     cn : [
36643                         {
36644                             tag : 'div',
36645                             cls : 'roo-document-slider-header-title'
36646                         }
36647                     ]
36648                 },
36649                 {
36650                     tag : 'div',
36651                     cls : 'roo-document-slider-body',
36652                     cn : [
36653                         {
36654                             tag : 'div',
36655                             cls : 'roo-document-slider-prev',
36656                             cn : [
36657                                 {
36658                                     tag : 'i',
36659                                     cls : 'fa fa-chevron-left'
36660                                 }
36661                             ]
36662                         },
36663                         {
36664                             tag : 'div',
36665                             cls : 'roo-document-slider-thumb',
36666                             cn : [
36667                                 {
36668                                     tag : 'img',
36669                                     cls : 'roo-document-slider-image'
36670                                 }
36671                             ]
36672                         },
36673                         {
36674                             tag : 'div',
36675                             cls : 'roo-document-slider-next',
36676                             cn : [
36677                                 {
36678                                     tag : 'i',
36679                                     cls : 'fa fa-chevron-right'
36680                                 }
36681                             ]
36682                         }
36683                     ]
36684                 }
36685             ]
36686         };
36687         
36688         return cfg;
36689     },
36690     
36691     initEvents : function()
36692     {
36693         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36694         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36695         
36696         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36697         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36698         
36699         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36700         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36701         
36702         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36703         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36704         
36705         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36706         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36707         
36708         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36709         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36710         
36711         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36712         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36713         
36714         this.thumbEl.on('click', this.onClick, this);
36715         
36716         this.prevIndicator.on('click', this.prev, this);
36717         
36718         this.nextIndicator.on('click', this.next, this);
36719         
36720     },
36721     
36722     initial : function()
36723     {
36724         if(this.files.length){
36725             this.indicator = 1;
36726             this.update()
36727         }
36728         
36729         this.fireEvent('initial', this);
36730     },
36731     
36732     update : function()
36733     {
36734         this.imageEl.attr('src', this.files[this.indicator - 1]);
36735         
36736         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36737         
36738         this.prevIndicator.show();
36739         
36740         if(this.indicator == 1){
36741             this.prevIndicator.hide();
36742         }
36743         
36744         this.nextIndicator.show();
36745         
36746         if(this.indicator == this.files.length){
36747             this.nextIndicator.hide();
36748         }
36749         
36750         this.thumbEl.scrollTo('top');
36751         
36752         this.fireEvent('update', this);
36753     },
36754     
36755     onClick : function(e)
36756     {
36757         e.preventDefault();
36758         
36759         this.fireEvent('click', this);
36760     },
36761     
36762     prev : function(e)
36763     {
36764         e.preventDefault();
36765         
36766         this.indicator = Math.max(1, this.indicator - 1);
36767         
36768         this.update();
36769     },
36770     
36771     next : function(e)
36772     {
36773         e.preventDefault();
36774         
36775         this.indicator = Math.min(this.files.length, this.indicator + 1);
36776         
36777         this.update();
36778     }
36779 });
36780 /*
36781  * - LGPL
36782  *
36783  * RadioSet
36784  *
36785  *
36786  */
36787
36788 /**
36789  * @class Roo.bootstrap.RadioSet
36790  * @extends Roo.bootstrap.Input
36791  * Bootstrap RadioSet class
36792  * @cfg {String} indicatorpos (left|right) default left
36793  * @cfg {Boolean} inline (true|false) inline the element (default true)
36794  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36795  * @constructor
36796  * Create a new RadioSet
36797  * @param {Object} config The config object
36798  */
36799
36800 Roo.bootstrap.RadioSet = function(config){
36801     
36802     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36803     
36804     this.radioes = [];
36805     
36806     Roo.bootstrap.RadioSet.register(this);
36807     
36808     this.addEvents({
36809         /**
36810         * @event check
36811         * Fires when the element is checked or unchecked.
36812         * @param {Roo.bootstrap.RadioSet} this This radio
36813         * @param {Roo.bootstrap.Radio} item The checked item
36814         */
36815        check : true,
36816        /**
36817         * @event click
36818         * Fires when the element is click.
36819         * @param {Roo.bootstrap.RadioSet} this This radio set
36820         * @param {Roo.bootstrap.Radio} item The checked item
36821         * @param {Roo.EventObject} e The event object
36822         */
36823        click : true
36824     });
36825     
36826 };
36827
36828 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36829
36830     radioes : false,
36831     
36832     inline : true,
36833     
36834     weight : '',
36835     
36836     indicatorpos : 'left',
36837     
36838     getAutoCreate : function()
36839     {
36840         var label = {
36841             tag : 'label',
36842             cls : 'roo-radio-set-label',
36843             cn : [
36844                 {
36845                     tag : 'span',
36846                     html : this.fieldLabel
36847                 }
36848             ]
36849         };
36850         if (Roo.bootstrap.version == 3) {
36851             
36852             
36853             if(this.indicatorpos == 'left'){
36854                 label.cn.unshift({
36855                     tag : 'i',
36856                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36857                     tooltip : 'This field is required'
36858                 });
36859             } else {
36860                 label.cn.push({
36861                     tag : 'i',
36862                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36863                     tooltip : 'This field is required'
36864                 });
36865             }
36866         }
36867         var items = {
36868             tag : 'div',
36869             cls : 'roo-radio-set-items'
36870         };
36871         
36872         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36873         
36874         if (align === 'left' && this.fieldLabel.length) {
36875             
36876             items = {
36877                 cls : "roo-radio-set-right", 
36878                 cn: [
36879                     items
36880                 ]
36881             };
36882             
36883             if(this.labelWidth > 12){
36884                 label.style = "width: " + this.labelWidth + 'px';
36885             }
36886             
36887             if(this.labelWidth < 13 && this.labelmd == 0){
36888                 this.labelmd = this.labelWidth;
36889             }
36890             
36891             if(this.labellg > 0){
36892                 label.cls += ' col-lg-' + this.labellg;
36893                 items.cls += ' col-lg-' + (12 - this.labellg);
36894             }
36895             
36896             if(this.labelmd > 0){
36897                 label.cls += ' col-md-' + this.labelmd;
36898                 items.cls += ' col-md-' + (12 - this.labelmd);
36899             }
36900             
36901             if(this.labelsm > 0){
36902                 label.cls += ' col-sm-' + this.labelsm;
36903                 items.cls += ' col-sm-' + (12 - this.labelsm);
36904             }
36905             
36906             if(this.labelxs > 0){
36907                 label.cls += ' col-xs-' + this.labelxs;
36908                 items.cls += ' col-xs-' + (12 - this.labelxs);
36909             }
36910         }
36911         
36912         var cfg = {
36913             tag : 'div',
36914             cls : 'roo-radio-set',
36915             cn : [
36916                 {
36917                     tag : 'input',
36918                     cls : 'roo-radio-set-input',
36919                     type : 'hidden',
36920                     name : this.name,
36921                     value : this.value ? this.value :  ''
36922                 },
36923                 label,
36924                 items
36925             ]
36926         };
36927         
36928         if(this.weight.length){
36929             cfg.cls += ' roo-radio-' + this.weight;
36930         }
36931         
36932         if(this.inline) {
36933             cfg.cls += ' roo-radio-set-inline';
36934         }
36935         
36936         var settings=this;
36937         ['xs','sm','md','lg'].map(function(size){
36938             if (settings[size]) {
36939                 cfg.cls += ' col-' + size + '-' + settings[size];
36940             }
36941         });
36942         
36943         return cfg;
36944         
36945     },
36946
36947     initEvents : function()
36948     {
36949         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36950         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36951         
36952         if(!this.fieldLabel.length){
36953             this.labelEl.hide();
36954         }
36955         
36956         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36957         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.indicator = this.indicatorEl();
36960         
36961         if(this.indicator){
36962             this.indicator.addClass('invisible');
36963         }
36964         
36965         this.originalValue = this.getValue();
36966         
36967     },
36968     
36969     inputEl: function ()
36970     {
36971         return this.el.select('.roo-radio-set-input', true).first();
36972     },
36973     
36974     getChildContainer : function()
36975     {
36976         return this.itemsEl;
36977     },
36978     
36979     register : function(item)
36980     {
36981         this.radioes.push(item);
36982         
36983     },
36984     
36985     validate : function()
36986     {   
36987         if(this.getVisibilityEl().hasClass('hidden')){
36988             return true;
36989         }
36990         
36991         var valid = false;
36992         
36993         Roo.each(this.radioes, function(i){
36994             if(!i.checked){
36995                 return;
36996             }
36997             
36998             valid = true;
36999             return false;
37000         });
37001         
37002         if(this.allowBlank) {
37003             return true;
37004         }
37005         
37006         if(this.disabled || valid){
37007             this.markValid();
37008             return true;
37009         }
37010         
37011         this.markInvalid();
37012         return false;
37013         
37014     },
37015     
37016     markValid : function()
37017     {
37018         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37019             this.indicatorEl().removeClass('visible');
37020             this.indicatorEl().addClass('invisible');
37021         }
37022         
37023         
37024         if (Roo.bootstrap.version == 3) {
37025             this.el.removeClass([this.invalidClass, this.validClass]);
37026             this.el.addClass(this.validClass);
37027         } else {
37028             this.el.removeClass(['is-invalid','is-valid']);
37029             this.el.addClass(['is-valid']);
37030         }
37031         this.fireEvent('valid', this);
37032     },
37033     
37034     markInvalid : function(msg)
37035     {
37036         if(this.allowBlank || this.disabled){
37037             return;
37038         }
37039         
37040         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37041             this.indicatorEl().removeClass('invisible');
37042             this.indicatorEl().addClass('visible');
37043         }
37044         if (Roo.bootstrap.version == 3) {
37045             this.el.removeClass([this.invalidClass, this.validClass]);
37046             this.el.addClass(this.invalidClass);
37047         } else {
37048             this.el.removeClass(['is-invalid','is-valid']);
37049             this.el.addClass(['is-invalid']);
37050         }
37051         
37052         this.fireEvent('invalid', this, msg);
37053         
37054     },
37055     
37056     setValue : function(v, suppressEvent)
37057     {   
37058         if(this.value === v){
37059             return;
37060         }
37061         
37062         this.value = v;
37063         
37064         if(this.rendered){
37065             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37066         }
37067         
37068         Roo.each(this.radioes, function(i){
37069             i.checked = false;
37070             i.el.removeClass('checked');
37071         });
37072         
37073         Roo.each(this.radioes, function(i){
37074             
37075             if(i.value === v || i.value.toString() === v.toString()){
37076                 i.checked = true;
37077                 i.el.addClass('checked');
37078                 
37079                 if(suppressEvent !== true){
37080                     this.fireEvent('check', this, i);
37081                 }
37082                 
37083                 return false;
37084             }
37085             
37086         }, this);
37087         
37088         this.validate();
37089     },
37090     
37091     clearInvalid : function(){
37092         
37093         if(!this.el || this.preventMark){
37094             return;
37095         }
37096         
37097         this.el.removeClass([this.invalidClass]);
37098         
37099         this.fireEvent('valid', this);
37100     }
37101     
37102 });
37103
37104 Roo.apply(Roo.bootstrap.RadioSet, {
37105     
37106     groups: {},
37107     
37108     register : function(set)
37109     {
37110         this.groups[set.name] = set;
37111     },
37112     
37113     get: function(name) 
37114     {
37115         if (typeof(this.groups[name]) == 'undefined') {
37116             return false;
37117         }
37118         
37119         return this.groups[name] ;
37120     }
37121     
37122 });
37123 /*
37124  * Based on:
37125  * Ext JS Library 1.1.1
37126  * Copyright(c) 2006-2007, Ext JS, LLC.
37127  *
37128  * Originally Released Under LGPL - original licence link has changed is not relivant.
37129  *
37130  * Fork - LGPL
37131  * <script type="text/javascript">
37132  */
37133
37134
37135 /**
37136  * @class Roo.bootstrap.SplitBar
37137  * @extends Roo.util.Observable
37138  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37139  * <br><br>
37140  * Usage:
37141  * <pre><code>
37142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37143                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37145 split.minSize = 100;
37146 split.maxSize = 600;
37147 split.animate = true;
37148 split.on('moved', splitterMoved);
37149 </code></pre>
37150  * @constructor
37151  * Create a new SplitBar
37152  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37153  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37154  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37155  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37156                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37157                         position of the SplitBar).
37158  */
37159 Roo.bootstrap.SplitBar = function(cfg){
37160     
37161     /** @private */
37162     
37163     //{
37164     //  dragElement : elm
37165     //  resizingElement: el,
37166         // optional..
37167     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37168     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37169         // existingProxy ???
37170     //}
37171     
37172     this.el = Roo.get(cfg.dragElement, true);
37173     this.el.dom.unselectable = "on";
37174     /** @private */
37175     this.resizingEl = Roo.get(cfg.resizingElement, true);
37176
37177     /**
37178      * @private
37179      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37180      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37181      * @type Number
37182      */
37183     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37184     
37185     /**
37186      * The minimum size of the resizing element. (Defaults to 0)
37187      * @type Number
37188      */
37189     this.minSize = 0;
37190     
37191     /**
37192      * The maximum size of the resizing element. (Defaults to 2000)
37193      * @type Number
37194      */
37195     this.maxSize = 2000;
37196     
37197     /**
37198      * Whether to animate the transition to the new size
37199      * @type Boolean
37200      */
37201     this.animate = false;
37202     
37203     /**
37204      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37205      * @type Boolean
37206      */
37207     this.useShim = false;
37208     
37209     /** @private */
37210     this.shim = null;
37211     
37212     if(!cfg.existingProxy){
37213         /** @private */
37214         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37215     }else{
37216         this.proxy = Roo.get(cfg.existingProxy).dom;
37217     }
37218     /** @private */
37219     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37220     
37221     /** @private */
37222     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37223     
37224     /** @private */
37225     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37226     
37227     /** @private */
37228     this.dragSpecs = {};
37229     
37230     /**
37231      * @private The adapter to use to positon and resize elements
37232      */
37233     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37234     this.adapter.init(this);
37235     
37236     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37237         /** @private */
37238         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37239         this.el.addClass("roo-splitbar-h");
37240     }else{
37241         /** @private */
37242         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37243         this.el.addClass("roo-splitbar-v");
37244     }
37245     
37246     this.addEvents({
37247         /**
37248          * @event resize
37249          * Fires when the splitter is moved (alias for {@link #event-moved})
37250          * @param {Roo.bootstrap.SplitBar} this
37251          * @param {Number} newSize the new width or height
37252          */
37253         "resize" : true,
37254         /**
37255          * @event moved
37256          * Fires when the splitter is moved
37257          * @param {Roo.bootstrap.SplitBar} this
37258          * @param {Number} newSize the new width or height
37259          */
37260         "moved" : true,
37261         /**
37262          * @event beforeresize
37263          * Fires before the splitter is dragged
37264          * @param {Roo.bootstrap.SplitBar} this
37265          */
37266         "beforeresize" : true,
37267
37268         "beforeapply" : true
37269     });
37270
37271     Roo.util.Observable.call(this);
37272 };
37273
37274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37275     onStartProxyDrag : function(x, y){
37276         this.fireEvent("beforeresize", this);
37277         if(!this.overlay){
37278             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37279             o.unselectable();
37280             o.enableDisplayMode("block");
37281             // all splitbars share the same overlay
37282             Roo.bootstrap.SplitBar.prototype.overlay = o;
37283         }
37284         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37285         this.overlay.show();
37286         Roo.get(this.proxy).setDisplayed("block");
37287         var size = this.adapter.getElementSize(this);
37288         this.activeMinSize = this.getMinimumSize();;
37289         this.activeMaxSize = this.getMaximumSize();;
37290         var c1 = size - this.activeMinSize;
37291         var c2 = Math.max(this.activeMaxSize - size, 0);
37292         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37293             this.dd.resetConstraints();
37294             this.dd.setXConstraint(
37295                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37296                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37297             );
37298             this.dd.setYConstraint(0, 0);
37299         }else{
37300             this.dd.resetConstraints();
37301             this.dd.setXConstraint(0, 0);
37302             this.dd.setYConstraint(
37303                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37304                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37305             );
37306          }
37307         this.dragSpecs.startSize = size;
37308         this.dragSpecs.startPoint = [x, y];
37309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37310     },
37311     
37312     /** 
37313      * @private Called after the drag operation by the DDProxy
37314      */
37315     onEndProxyDrag : function(e){
37316         Roo.get(this.proxy).setDisplayed(false);
37317         var endPoint = Roo.lib.Event.getXY(e);
37318         if(this.overlay){
37319             this.overlay.hide();
37320         }
37321         var newSize;
37322         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37323             newSize = this.dragSpecs.startSize + 
37324                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37325                     endPoint[0] - this.dragSpecs.startPoint[0] :
37326                     this.dragSpecs.startPoint[0] - endPoint[0]
37327                 );
37328         }else{
37329             newSize = this.dragSpecs.startSize + 
37330                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37331                     endPoint[1] - this.dragSpecs.startPoint[1] :
37332                     this.dragSpecs.startPoint[1] - endPoint[1]
37333                 );
37334         }
37335         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37336         if(newSize != this.dragSpecs.startSize){
37337             if(this.fireEvent('beforeapply', this, newSize) !== false){
37338                 this.adapter.setElementSize(this, newSize);
37339                 this.fireEvent("moved", this, newSize);
37340                 this.fireEvent("resize", this, newSize);
37341             }
37342         }
37343     },
37344     
37345     /**
37346      * Get the adapter this SplitBar uses
37347      * @return The adapter object
37348      */
37349     getAdapter : function(){
37350         return this.adapter;
37351     },
37352     
37353     /**
37354      * Set the adapter this SplitBar uses
37355      * @param {Object} adapter A SplitBar adapter object
37356      */
37357     setAdapter : function(adapter){
37358         this.adapter = adapter;
37359         this.adapter.init(this);
37360     },
37361     
37362     /**
37363      * Gets the minimum size for the resizing element
37364      * @return {Number} The minimum size
37365      */
37366     getMinimumSize : function(){
37367         return this.minSize;
37368     },
37369     
37370     /**
37371      * Sets the minimum size for the resizing element
37372      * @param {Number} minSize The minimum size
37373      */
37374     setMinimumSize : function(minSize){
37375         this.minSize = minSize;
37376     },
37377     
37378     /**
37379      * Gets the maximum size for the resizing element
37380      * @return {Number} The maximum size
37381      */
37382     getMaximumSize : function(){
37383         return this.maxSize;
37384     },
37385     
37386     /**
37387      * Sets the maximum size for the resizing element
37388      * @param {Number} maxSize The maximum size
37389      */
37390     setMaximumSize : function(maxSize){
37391         this.maxSize = maxSize;
37392     },
37393     
37394     /**
37395      * Sets the initialize size for the resizing element
37396      * @param {Number} size The initial size
37397      */
37398     setCurrentSize : function(size){
37399         var oldAnimate = this.animate;
37400         this.animate = false;
37401         this.adapter.setElementSize(this, size);
37402         this.animate = oldAnimate;
37403     },
37404     
37405     /**
37406      * Destroy this splitbar. 
37407      * @param {Boolean} removeEl True to remove the element
37408      */
37409     destroy : function(removeEl){
37410         if(this.shim){
37411             this.shim.remove();
37412         }
37413         this.dd.unreg();
37414         this.proxy.parentNode.removeChild(this.proxy);
37415         if(removeEl){
37416             this.el.remove();
37417         }
37418     }
37419 });
37420
37421 /**
37422  * @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.
37423  */
37424 Roo.bootstrap.SplitBar.createProxy = function(dir){
37425     var proxy = new Roo.Element(document.createElement("div"));
37426     proxy.unselectable();
37427     var cls = 'roo-splitbar-proxy';
37428     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37429     document.body.appendChild(proxy.dom);
37430     return proxy.dom;
37431 };
37432
37433 /** 
37434  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37435  * Default Adapter. It assumes the splitter and resizing element are not positioned
37436  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37437  */
37438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37439 };
37440
37441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37442     // do nothing for now
37443     init : function(s){
37444     
37445     },
37446     /**
37447      * Called before drag operations to get the current size of the resizing element. 
37448      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37449      */
37450      getElementSize : function(s){
37451         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37452             return s.resizingEl.getWidth();
37453         }else{
37454             return s.resizingEl.getHeight();
37455         }
37456     },
37457     
37458     /**
37459      * Called after drag operations to set the size of the resizing element.
37460      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37461      * @param {Number} newSize The new size to set
37462      * @param {Function} onComplete A function to be invoked when resizing is complete
37463      */
37464     setElementSize : function(s, newSize, onComplete){
37465         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37466             if(!s.animate){
37467                 s.resizingEl.setWidth(newSize);
37468                 if(onComplete){
37469                     onComplete(s, newSize);
37470                 }
37471             }else{
37472                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37473             }
37474         }else{
37475             
37476             if(!s.animate){
37477                 s.resizingEl.setHeight(newSize);
37478                 if(onComplete){
37479                     onComplete(s, newSize);
37480                 }
37481             }else{
37482                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37483             }
37484         }
37485     }
37486 };
37487
37488 /** 
37489  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37490  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37491  * Adapter that  moves the splitter element to align with the resized sizing element. 
37492  * Used with an absolute positioned SplitBar.
37493  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37494  * document.body, make sure you assign an id to the body element.
37495  */
37496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37497     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37498     this.container = Roo.get(container);
37499 };
37500
37501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37502     init : function(s){
37503         this.basic.init(s);
37504     },
37505     
37506     getElementSize : function(s){
37507         return this.basic.getElementSize(s);
37508     },
37509     
37510     setElementSize : function(s, newSize, onComplete){
37511         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37512     },
37513     
37514     moveSplitter : function(s){
37515         var yes = Roo.bootstrap.SplitBar;
37516         switch(s.placement){
37517             case yes.LEFT:
37518                 s.el.setX(s.resizingEl.getRight());
37519                 break;
37520             case yes.RIGHT:
37521                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37522                 break;
37523             case yes.TOP:
37524                 s.el.setY(s.resizingEl.getBottom());
37525                 break;
37526             case yes.BOTTOM:
37527                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37528                 break;
37529         }
37530     }
37531 };
37532
37533 /**
37534  * Orientation constant - Create a vertical SplitBar
37535  * @static
37536  * @type Number
37537  */
37538 Roo.bootstrap.SplitBar.VERTICAL = 1;
37539
37540 /**
37541  * Orientation constant - Create a horizontal SplitBar
37542  * @static
37543  * @type Number
37544  */
37545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37546
37547 /**
37548  * Placement constant - The resizing element is to the left of the splitter element
37549  * @static
37550  * @type Number
37551  */
37552 Roo.bootstrap.SplitBar.LEFT = 1;
37553
37554 /**
37555  * Placement constant - The resizing element is to the right of the splitter element
37556  * @static
37557  * @type Number
37558  */
37559 Roo.bootstrap.SplitBar.RIGHT = 2;
37560
37561 /**
37562  * Placement constant - The resizing element is positioned above the splitter element
37563  * @static
37564  * @type Number
37565  */
37566 Roo.bootstrap.SplitBar.TOP = 3;
37567
37568 /**
37569  * Placement constant - The resizing element is positioned under splitter element
37570  * @static
37571  * @type Number
37572  */
37573 Roo.bootstrap.SplitBar.BOTTOM = 4;
37574 Roo.namespace("Roo.bootstrap.layout");/*
37575  * Based on:
37576  * Ext JS Library 1.1.1
37577  * Copyright(c) 2006-2007, Ext JS, LLC.
37578  *
37579  * Originally Released Under LGPL - original licence link has changed is not relivant.
37580  *
37581  * Fork - LGPL
37582  * <script type="text/javascript">
37583  */
37584
37585 /**
37586  * @class Roo.bootstrap.layout.Manager
37587  * @extends Roo.bootstrap.Component
37588  * Base class for layout managers.
37589  */
37590 Roo.bootstrap.layout.Manager = function(config)
37591 {
37592     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37593
37594
37595
37596
37597
37598     /** false to disable window resize monitoring @type Boolean */
37599     this.monitorWindowResize = true;
37600     this.regions = {};
37601     this.addEvents({
37602         /**
37603          * @event layout
37604          * Fires when a layout is performed.
37605          * @param {Roo.LayoutManager} this
37606          */
37607         "layout" : true,
37608         /**
37609          * @event regionresized
37610          * Fires when the user resizes a region.
37611          * @param {Roo.LayoutRegion} region The resized region
37612          * @param {Number} newSize The new size (width for east/west, height for north/south)
37613          */
37614         "regionresized" : true,
37615         /**
37616          * @event regioncollapsed
37617          * Fires when a region is collapsed.
37618          * @param {Roo.LayoutRegion} region The collapsed region
37619          */
37620         "regioncollapsed" : true,
37621         /**
37622          * @event regionexpanded
37623          * Fires when a region is expanded.
37624          * @param {Roo.LayoutRegion} region The expanded region
37625          */
37626         "regionexpanded" : true
37627     });
37628     this.updating = false;
37629
37630     if (config.el) {
37631         this.el = Roo.get(config.el);
37632         this.initEvents();
37633     }
37634
37635 };
37636
37637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37638
37639
37640     regions : null,
37641
37642     monitorWindowResize : true,
37643
37644
37645     updating : false,
37646
37647
37648     onRender : function(ct, position)
37649     {
37650         if(!this.el){
37651             this.el = Roo.get(ct);
37652             this.initEvents();
37653         }
37654         //this.fireEvent('render',this);
37655     },
37656
37657
37658     initEvents: function()
37659     {
37660
37661
37662         // ie scrollbar fix
37663         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37664             document.body.scroll = "no";
37665         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37666             this.el.position('relative');
37667         }
37668         this.id = this.el.id;
37669         this.el.addClass("roo-layout-container");
37670         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37671         if(this.el.dom != document.body ) {
37672             this.el.on('resize', this.layout,this);
37673             this.el.on('show', this.layout,this);
37674         }
37675
37676     },
37677
37678     /**
37679      * Returns true if this layout is currently being updated
37680      * @return {Boolean}
37681      */
37682     isUpdating : function(){
37683         return this.updating;
37684     },
37685
37686     /**
37687      * Suspend the LayoutManager from doing auto-layouts while
37688      * making multiple add or remove calls
37689      */
37690     beginUpdate : function(){
37691         this.updating = true;
37692     },
37693
37694     /**
37695      * Restore auto-layouts and optionally disable the manager from performing a layout
37696      * @param {Boolean} noLayout true to disable a layout update
37697      */
37698     endUpdate : function(noLayout){
37699         this.updating = false;
37700         if(!noLayout){
37701             this.layout();
37702         }
37703     },
37704
37705     layout: function(){
37706         // abstract...
37707     },
37708
37709     onRegionResized : function(region, newSize){
37710         this.fireEvent("regionresized", region, newSize);
37711         this.layout();
37712     },
37713
37714     onRegionCollapsed : function(region){
37715         this.fireEvent("regioncollapsed", region);
37716     },
37717
37718     onRegionExpanded : function(region){
37719         this.fireEvent("regionexpanded", region);
37720     },
37721
37722     /**
37723      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37724      * performs box-model adjustments.
37725      * @return {Object} The size as an object {width: (the width), height: (the height)}
37726      */
37727     getViewSize : function()
37728     {
37729         var size;
37730         if(this.el.dom != document.body){
37731             size = this.el.getSize();
37732         }else{
37733             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37734         }
37735         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37736         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37737         return size;
37738     },
37739
37740     /**
37741      * Returns the Element this layout is bound to.
37742      * @return {Roo.Element}
37743      */
37744     getEl : function(){
37745         return this.el;
37746     },
37747
37748     /**
37749      * Returns the specified region.
37750      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37751      * @return {Roo.LayoutRegion}
37752      */
37753     getRegion : function(target){
37754         return this.regions[target.toLowerCase()];
37755     },
37756
37757     onWindowResize : function(){
37758         if(this.monitorWindowResize){
37759             this.layout();
37760         }
37761     }
37762 });
37763 /*
37764  * Based on:
37765  * Ext JS Library 1.1.1
37766  * Copyright(c) 2006-2007, Ext JS, LLC.
37767  *
37768  * Originally Released Under LGPL - original licence link has changed is not relivant.
37769  *
37770  * Fork - LGPL
37771  * <script type="text/javascript">
37772  */
37773 /**
37774  * @class Roo.bootstrap.layout.Border
37775  * @extends Roo.bootstrap.layout.Manager
37776  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37777  * please see: examples/bootstrap/nested.html<br><br>
37778  
37779 <b>The container the layout is rendered into can be either the body element or any other element.
37780 If it is not the body element, the container needs to either be an absolute positioned element,
37781 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37782 the container size if it is not the body element.</b>
37783
37784 * @constructor
37785 * Create a new Border
37786 * @param {Object} config Configuration options
37787  */
37788 Roo.bootstrap.layout.Border = function(config){
37789     config = config || {};
37790     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37791     
37792     
37793     
37794     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37795         if(config[region]){
37796             config[region].region = region;
37797             this.addRegion(config[region]);
37798         }
37799     },this);
37800     
37801 };
37802
37803 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37804
37805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37806     
37807     parent : false, // this might point to a 'nest' or a ???
37808     
37809     /**
37810      * Creates and adds a new region if it doesn't already exist.
37811      * @param {String} target The target region key (north, south, east, west or center).
37812      * @param {Object} config The regions config object
37813      * @return {BorderLayoutRegion} The new region
37814      */
37815     addRegion : function(config)
37816     {
37817         if(!this.regions[config.region]){
37818             var r = this.factory(config);
37819             this.bindRegion(r);
37820         }
37821         return this.regions[config.region];
37822     },
37823
37824     // private (kinda)
37825     bindRegion : function(r){
37826         this.regions[r.config.region] = r;
37827         
37828         r.on("visibilitychange",    this.layout, this);
37829         r.on("paneladded",          this.layout, this);
37830         r.on("panelremoved",        this.layout, this);
37831         r.on("invalidated",         this.layout, this);
37832         r.on("resized",             this.onRegionResized, this);
37833         r.on("collapsed",           this.onRegionCollapsed, this);
37834         r.on("expanded",            this.onRegionExpanded, this);
37835     },
37836
37837     /**
37838      * Performs a layout update.
37839      */
37840     layout : function()
37841     {
37842         if(this.updating) {
37843             return;
37844         }
37845         
37846         // render all the rebions if they have not been done alreayd?
37847         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37848             if(this.regions[region] && !this.regions[region].bodyEl){
37849                 this.regions[region].onRender(this.el)
37850             }
37851         },this);
37852         
37853         var size = this.getViewSize();
37854         var w = size.width;
37855         var h = size.height;
37856         var centerW = w;
37857         var centerH = h;
37858         var centerY = 0;
37859         var centerX = 0;
37860         //var x = 0, y = 0;
37861
37862         var rs = this.regions;
37863         var north = rs["north"];
37864         var south = rs["south"]; 
37865         var west = rs["west"];
37866         var east = rs["east"];
37867         var center = rs["center"];
37868         //if(this.hideOnLayout){ // not supported anymore
37869             //c.el.setStyle("display", "none");
37870         //}
37871         if(north && north.isVisible()){
37872             var b = north.getBox();
37873             var m = north.getMargins();
37874             b.width = w - (m.left+m.right);
37875             b.x = m.left;
37876             b.y = m.top;
37877             centerY = b.height + b.y + m.bottom;
37878             centerH -= centerY;
37879             north.updateBox(this.safeBox(b));
37880         }
37881         if(south && south.isVisible()){
37882             var b = south.getBox();
37883             var m = south.getMargins();
37884             b.width = w - (m.left+m.right);
37885             b.x = m.left;
37886             var totalHeight = (b.height + m.top + m.bottom);
37887             b.y = h - totalHeight + m.top;
37888             centerH -= totalHeight;
37889             south.updateBox(this.safeBox(b));
37890         }
37891         if(west && west.isVisible()){
37892             var b = west.getBox();
37893             var m = west.getMargins();
37894             b.height = centerH - (m.top+m.bottom);
37895             b.x = m.left;
37896             b.y = centerY + m.top;
37897             var totalWidth = (b.width + m.left + m.right);
37898             centerX += totalWidth;
37899             centerW -= totalWidth;
37900             west.updateBox(this.safeBox(b));
37901         }
37902         if(east && east.isVisible()){
37903             var b = east.getBox();
37904             var m = east.getMargins();
37905             b.height = centerH - (m.top+m.bottom);
37906             var totalWidth = (b.width + m.left + m.right);
37907             b.x = w - totalWidth + m.left;
37908             b.y = centerY + m.top;
37909             centerW -= totalWidth;
37910             east.updateBox(this.safeBox(b));
37911         }
37912         if(center){
37913             var m = center.getMargins();
37914             var centerBox = {
37915                 x: centerX + m.left,
37916                 y: centerY + m.top,
37917                 width: centerW - (m.left+m.right),
37918                 height: centerH - (m.top+m.bottom)
37919             };
37920             //if(this.hideOnLayout){
37921                 //center.el.setStyle("display", "block");
37922             //}
37923             center.updateBox(this.safeBox(centerBox));
37924         }
37925         this.el.repaint();
37926         this.fireEvent("layout", this);
37927     },
37928
37929     // private
37930     safeBox : function(box){
37931         box.width = Math.max(0, box.width);
37932         box.height = Math.max(0, box.height);
37933         return box;
37934     },
37935
37936     /**
37937      * Adds a ContentPanel (or subclass) to this layout.
37938      * @param {String} target The target region key (north, south, east, west or center).
37939      * @param {Roo.ContentPanel} panel The panel to add
37940      * @return {Roo.ContentPanel} The added panel
37941      */
37942     add : function(target, panel){
37943          
37944         target = target.toLowerCase();
37945         return this.regions[target].add(panel);
37946     },
37947
37948     /**
37949      * Remove a ContentPanel (or subclass) to this layout.
37950      * @param {String} target The target region key (north, south, east, west or center).
37951      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37952      * @return {Roo.ContentPanel} The removed panel
37953      */
37954     remove : function(target, panel){
37955         target = target.toLowerCase();
37956         return this.regions[target].remove(panel);
37957     },
37958
37959     /**
37960      * Searches all regions for a panel with the specified id
37961      * @param {String} panelId
37962      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37963      */
37964     findPanel : function(panelId){
37965         var rs = this.regions;
37966         for(var target in rs){
37967             if(typeof rs[target] != "function"){
37968                 var p = rs[target].getPanel(panelId);
37969                 if(p){
37970                     return p;
37971                 }
37972             }
37973         }
37974         return null;
37975     },
37976
37977     /**
37978      * Searches all regions for a panel with the specified id and activates (shows) it.
37979      * @param {String/ContentPanel} panelId The panels id or the panel itself
37980      * @return {Roo.ContentPanel} The shown panel or null
37981      */
37982     showPanel : function(panelId) {
37983       var rs = this.regions;
37984       for(var target in rs){
37985          var r = rs[target];
37986          if(typeof r != "function"){
37987             if(r.hasPanel(panelId)){
37988                return r.showPanel(panelId);
37989             }
37990          }
37991       }
37992       return null;
37993    },
37994
37995    /**
37996      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37997      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37998      */
37999    /*
38000     restoreState : function(provider){
38001         if(!provider){
38002             provider = Roo.state.Manager;
38003         }
38004         var sm = new Roo.LayoutStateManager();
38005         sm.init(this, provider);
38006     },
38007 */
38008  
38009  
38010     /**
38011      * Adds a xtype elements to the layout.
38012      * <pre><code>
38013
38014 layout.addxtype({
38015        xtype : 'ContentPanel',
38016        region: 'west',
38017        items: [ .... ]
38018    }
38019 );
38020
38021 layout.addxtype({
38022         xtype : 'NestedLayoutPanel',
38023         region: 'west',
38024         layout: {
38025            center: { },
38026            west: { }   
38027         },
38028         items : [ ... list of content panels or nested layout panels.. ]
38029    }
38030 );
38031 </code></pre>
38032      * @param {Object} cfg Xtype definition of item to add.
38033      */
38034     addxtype : function(cfg)
38035     {
38036         // basically accepts a pannel...
38037         // can accept a layout region..!?!?
38038         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38039         
38040         
38041         // theory?  children can only be panels??
38042         
38043         //if (!cfg.xtype.match(/Panel$/)) {
38044         //    return false;
38045         //}
38046         var ret = false;
38047         
38048         if (typeof(cfg.region) == 'undefined') {
38049             Roo.log("Failed to add Panel, region was not set");
38050             Roo.log(cfg);
38051             return false;
38052         }
38053         var region = cfg.region;
38054         delete cfg.region;
38055         
38056           
38057         var xitems = [];
38058         if (cfg.items) {
38059             xitems = cfg.items;
38060             delete cfg.items;
38061         }
38062         var nb = false;
38063         
38064         if ( region == 'center') {
38065             Roo.log("Center: " + cfg.title);
38066         }
38067         
38068         
38069         switch(cfg.xtype) 
38070         {
38071             case 'Content':  // ContentPanel (el, cfg)
38072             case 'Scroll':  // ContentPanel (el, cfg)
38073             case 'View': 
38074                 cfg.autoCreate = cfg.autoCreate || true;
38075                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38076                 //} else {
38077                 //    var el = this.el.createChild();
38078                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38079                 //}
38080                 
38081                 this.add(region, ret);
38082                 break;
38083             
38084             /*
38085             case 'TreePanel': // our new panel!
38086                 cfg.el = this.el.createChild();
38087                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38088                 this.add(region, ret);
38089                 break;
38090             */
38091             
38092             case 'Nest': 
38093                 // create a new Layout (which is  a Border Layout...
38094                 
38095                 var clayout = cfg.layout;
38096                 clayout.el  = this.el.createChild();
38097                 clayout.items   = clayout.items  || [];
38098                 
38099                 delete cfg.layout;
38100                 
38101                 // replace this exitems with the clayout ones..
38102                 xitems = clayout.items;
38103                  
38104                 // force background off if it's in center...
38105                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38106                     cfg.background = false;
38107                 }
38108                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38109                 
38110                 
38111                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38112                 //console.log('adding nested layout panel '  + cfg.toSource());
38113                 this.add(region, ret);
38114                 nb = {}; /// find first...
38115                 break;
38116             
38117             case 'Grid':
38118                 
38119                 // needs grid and region
38120                 
38121                 //var el = this.getRegion(region).el.createChild();
38122                 /*
38123                  *var el = this.el.createChild();
38124                 // create the grid first...
38125                 cfg.grid.container = el;
38126                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38127                 */
38128                 
38129                 if (region == 'center' && this.active ) {
38130                     cfg.background = false;
38131                 }
38132                 
38133                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38134                 
38135                 this.add(region, ret);
38136                 /*
38137                 if (cfg.background) {
38138                     // render grid on panel activation (if panel background)
38139                     ret.on('activate', function(gp) {
38140                         if (!gp.grid.rendered) {
38141                     //        gp.grid.render(el);
38142                         }
38143                     });
38144                 } else {
38145                   //  cfg.grid.render(el);
38146                 }
38147                 */
38148                 break;
38149            
38150            
38151             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38152                 // it was the old xcomponent building that caused this before.
38153                 // espeically if border is the top element in the tree.
38154                 ret = this;
38155                 break; 
38156                 
38157                     
38158                 
38159                 
38160                 
38161             default:
38162                 /*
38163                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38164                     
38165                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38166                     this.add(region, ret);
38167                 } else {
38168                 */
38169                     Roo.log(cfg);
38170                     throw "Can not add '" + cfg.xtype + "' to Border";
38171                     return null;
38172              
38173                                 
38174              
38175         }
38176         this.beginUpdate();
38177         // add children..
38178         var region = '';
38179         var abn = {};
38180         Roo.each(xitems, function(i)  {
38181             region = nb && i.region ? i.region : false;
38182             
38183             var add = ret.addxtype(i);
38184            
38185             if (region) {
38186                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38187                 if (!i.background) {
38188                     abn[region] = nb[region] ;
38189                 }
38190             }
38191             
38192         });
38193         this.endUpdate();
38194
38195         // make the last non-background panel active..
38196         //if (nb) { Roo.log(abn); }
38197         if (nb) {
38198             
38199             for(var r in abn) {
38200                 region = this.getRegion(r);
38201                 if (region) {
38202                     // tried using nb[r], but it does not work..
38203                      
38204                     region.showPanel(abn[r]);
38205                    
38206                 }
38207             }
38208         }
38209         return ret;
38210         
38211     },
38212     
38213     
38214 // private
38215     factory : function(cfg)
38216     {
38217         
38218         var validRegions = Roo.bootstrap.layout.Border.regions;
38219
38220         var target = cfg.region;
38221         cfg.mgr = this;
38222         
38223         var r = Roo.bootstrap.layout;
38224         Roo.log(target);
38225         switch(target){
38226             case "north":
38227                 return new r.North(cfg);
38228             case "south":
38229                 return new r.South(cfg);
38230             case "east":
38231                 return new r.East(cfg);
38232             case "west":
38233                 return new r.West(cfg);
38234             case "center":
38235                 return new r.Center(cfg);
38236         }
38237         throw 'Layout region "'+target+'" not supported.';
38238     }
38239     
38240     
38241 });
38242  /*
38243  * Based on:
38244  * Ext JS Library 1.1.1
38245  * Copyright(c) 2006-2007, Ext JS, LLC.
38246  *
38247  * Originally Released Under LGPL - original licence link has changed is not relivant.
38248  *
38249  * Fork - LGPL
38250  * <script type="text/javascript">
38251  */
38252  
38253 /**
38254  * @class Roo.bootstrap.layout.Basic
38255  * @extends Roo.util.Observable
38256  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38257  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38258  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38259  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38260  * @cfg {string}   region  the region that it inhabits..
38261  * @cfg {bool}   skipConfig skip config?
38262  * 
38263
38264  */
38265 Roo.bootstrap.layout.Basic = function(config){
38266     
38267     this.mgr = config.mgr;
38268     
38269     this.position = config.region;
38270     
38271     var skipConfig = config.skipConfig;
38272     
38273     this.events = {
38274         /**
38275          * @scope Roo.BasicLayoutRegion
38276          */
38277         
38278         /**
38279          * @event beforeremove
38280          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38281          * @param {Roo.LayoutRegion} this
38282          * @param {Roo.ContentPanel} panel The panel
38283          * @param {Object} e The cancel event object
38284          */
38285         "beforeremove" : true,
38286         /**
38287          * @event invalidated
38288          * Fires when the layout for this region is changed.
38289          * @param {Roo.LayoutRegion} this
38290          */
38291         "invalidated" : true,
38292         /**
38293          * @event visibilitychange
38294          * Fires when this region is shown or hidden 
38295          * @param {Roo.LayoutRegion} this
38296          * @param {Boolean} visibility true or false
38297          */
38298         "visibilitychange" : true,
38299         /**
38300          * @event paneladded
38301          * Fires when a panel is added. 
38302          * @param {Roo.LayoutRegion} this
38303          * @param {Roo.ContentPanel} panel The panel
38304          */
38305         "paneladded" : true,
38306         /**
38307          * @event panelremoved
38308          * Fires when a panel is removed. 
38309          * @param {Roo.LayoutRegion} this
38310          * @param {Roo.ContentPanel} panel The panel
38311          */
38312         "panelremoved" : true,
38313         /**
38314          * @event beforecollapse
38315          * Fires when this region before collapse.
38316          * @param {Roo.LayoutRegion} this
38317          */
38318         "beforecollapse" : true,
38319         /**
38320          * @event collapsed
38321          * Fires when this region is collapsed.
38322          * @param {Roo.LayoutRegion} this
38323          */
38324         "collapsed" : true,
38325         /**
38326          * @event expanded
38327          * Fires when this region is expanded.
38328          * @param {Roo.LayoutRegion} this
38329          */
38330         "expanded" : true,
38331         /**
38332          * @event slideshow
38333          * Fires when this region is slid into view.
38334          * @param {Roo.LayoutRegion} this
38335          */
38336         "slideshow" : true,
38337         /**
38338          * @event slidehide
38339          * Fires when this region slides out of view. 
38340          * @param {Roo.LayoutRegion} this
38341          */
38342         "slidehide" : true,
38343         /**
38344          * @event panelactivated
38345          * Fires when a panel is activated. 
38346          * @param {Roo.LayoutRegion} this
38347          * @param {Roo.ContentPanel} panel The activated panel
38348          */
38349         "panelactivated" : true,
38350         /**
38351          * @event resized
38352          * Fires when the user resizes this region. 
38353          * @param {Roo.LayoutRegion} this
38354          * @param {Number} newSize The new size (width for east/west, height for north/south)
38355          */
38356         "resized" : true
38357     };
38358     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38359     this.panels = new Roo.util.MixedCollection();
38360     this.panels.getKey = this.getPanelId.createDelegate(this);
38361     this.box = null;
38362     this.activePanel = null;
38363     // ensure listeners are added...
38364     
38365     if (config.listeners || config.events) {
38366         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38367             listeners : config.listeners || {},
38368             events : config.events || {}
38369         });
38370     }
38371     
38372     if(skipConfig !== true){
38373         this.applyConfig(config);
38374     }
38375 };
38376
38377 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38378 {
38379     getPanelId : function(p){
38380         return p.getId();
38381     },
38382     
38383     applyConfig : function(config){
38384         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38385         this.config = config;
38386         
38387     },
38388     
38389     /**
38390      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38391      * the width, for horizontal (north, south) the height.
38392      * @param {Number} newSize The new width or height
38393      */
38394     resizeTo : function(newSize){
38395         var el = this.el ? this.el :
38396                  (this.activePanel ? this.activePanel.getEl() : null);
38397         if(el){
38398             switch(this.position){
38399                 case "east":
38400                 case "west":
38401                     el.setWidth(newSize);
38402                     this.fireEvent("resized", this, newSize);
38403                 break;
38404                 case "north":
38405                 case "south":
38406                     el.setHeight(newSize);
38407                     this.fireEvent("resized", this, newSize);
38408                 break;                
38409             }
38410         }
38411     },
38412     
38413     getBox : function(){
38414         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38415     },
38416     
38417     getMargins : function(){
38418         return this.margins;
38419     },
38420     
38421     updateBox : function(box){
38422         this.box = box;
38423         var el = this.activePanel.getEl();
38424         el.dom.style.left = box.x + "px";
38425         el.dom.style.top = box.y + "px";
38426         this.activePanel.setSize(box.width, box.height);
38427     },
38428     
38429     /**
38430      * Returns the container element for this region.
38431      * @return {Roo.Element}
38432      */
38433     getEl : function(){
38434         return this.activePanel;
38435     },
38436     
38437     /**
38438      * Returns true if this region is currently visible.
38439      * @return {Boolean}
38440      */
38441     isVisible : function(){
38442         return this.activePanel ? true : false;
38443     },
38444     
38445     setActivePanel : function(panel){
38446         panel = this.getPanel(panel);
38447         if(this.activePanel && this.activePanel != panel){
38448             this.activePanel.setActiveState(false);
38449             this.activePanel.getEl().setLeftTop(-10000,-10000);
38450         }
38451         this.activePanel = panel;
38452         panel.setActiveState(true);
38453         if(this.box){
38454             panel.setSize(this.box.width, this.box.height);
38455         }
38456         this.fireEvent("panelactivated", this, panel);
38457         this.fireEvent("invalidated");
38458     },
38459     
38460     /**
38461      * Show the specified panel.
38462      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38463      * @return {Roo.ContentPanel} The shown panel or null
38464      */
38465     showPanel : function(panel){
38466         panel = this.getPanel(panel);
38467         if(panel){
38468             this.setActivePanel(panel);
38469         }
38470         return panel;
38471     },
38472     
38473     /**
38474      * Get the active panel for this region.
38475      * @return {Roo.ContentPanel} The active panel or null
38476      */
38477     getActivePanel : function(){
38478         return this.activePanel;
38479     },
38480     
38481     /**
38482      * Add the passed ContentPanel(s)
38483      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38484      * @return {Roo.ContentPanel} The panel added (if only one was added)
38485      */
38486     add : function(panel){
38487         if(arguments.length > 1){
38488             for(var i = 0, len = arguments.length; i < len; i++) {
38489                 this.add(arguments[i]);
38490             }
38491             return null;
38492         }
38493         if(this.hasPanel(panel)){
38494             this.showPanel(panel);
38495             return panel;
38496         }
38497         var el = panel.getEl();
38498         if(el.dom.parentNode != this.mgr.el.dom){
38499             this.mgr.el.dom.appendChild(el.dom);
38500         }
38501         if(panel.setRegion){
38502             panel.setRegion(this);
38503         }
38504         this.panels.add(panel);
38505         el.setStyle("position", "absolute");
38506         if(!panel.background){
38507             this.setActivePanel(panel);
38508             if(this.config.initialSize && this.panels.getCount()==1){
38509                 this.resizeTo(this.config.initialSize);
38510             }
38511         }
38512         this.fireEvent("paneladded", this, panel);
38513         return panel;
38514     },
38515     
38516     /**
38517      * Returns true if the panel is in this region.
38518      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38519      * @return {Boolean}
38520      */
38521     hasPanel : function(panel){
38522         if(typeof panel == "object"){ // must be panel obj
38523             panel = panel.getId();
38524         }
38525         return this.getPanel(panel) ? true : false;
38526     },
38527     
38528     /**
38529      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38530      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38531      * @param {Boolean} preservePanel Overrides the config preservePanel option
38532      * @return {Roo.ContentPanel} The panel that was removed
38533      */
38534     remove : function(panel, preservePanel){
38535         panel = this.getPanel(panel);
38536         if(!panel){
38537             return null;
38538         }
38539         var e = {};
38540         this.fireEvent("beforeremove", this, panel, e);
38541         if(e.cancel === true){
38542             return null;
38543         }
38544         var panelId = panel.getId();
38545         this.panels.removeKey(panelId);
38546         return panel;
38547     },
38548     
38549     /**
38550      * Returns the panel specified or null if it's not in this region.
38551      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38552      * @return {Roo.ContentPanel}
38553      */
38554     getPanel : function(id){
38555         if(typeof id == "object"){ // must be panel obj
38556             return id;
38557         }
38558         return this.panels.get(id);
38559     },
38560     
38561     /**
38562      * Returns this regions position (north/south/east/west/center).
38563      * @return {String} 
38564      */
38565     getPosition: function(){
38566         return this.position;    
38567     }
38568 });/*
38569  * Based on:
38570  * Ext JS Library 1.1.1
38571  * Copyright(c) 2006-2007, Ext JS, LLC.
38572  *
38573  * Originally Released Under LGPL - original licence link has changed is not relivant.
38574  *
38575  * Fork - LGPL
38576  * <script type="text/javascript">
38577  */
38578  
38579 /**
38580  * @class Roo.bootstrap.layout.Region
38581  * @extends Roo.bootstrap.layout.Basic
38582  * This class represents a region in a layout manager.
38583  
38584  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38585  * @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})
38586  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38587  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38588  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38589  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38590  * @cfg {String}    title           The title for the region (overrides panel titles)
38591  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38592  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38593  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38594  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38595  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38596  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38597  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38598  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38599  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38600  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38601
38602  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38603  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38604  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38605  * @cfg {Number}    width           For East/West panels
38606  * @cfg {Number}    height          For North/South panels
38607  * @cfg {Boolean}   split           To show the splitter
38608  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38609  * 
38610  * @cfg {string}   cls             Extra CSS classes to add to region
38611  * 
38612  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38613  * @cfg {string}   region  the region that it inhabits..
38614  *
38615
38616  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38617  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38618
38619  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38620  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38621  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38622  */
38623 Roo.bootstrap.layout.Region = function(config)
38624 {
38625     this.applyConfig(config);
38626
38627     var mgr = config.mgr;
38628     var pos = config.region;
38629     config.skipConfig = true;
38630     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38631     
38632     if (mgr.el) {
38633         this.onRender(mgr.el);   
38634     }
38635      
38636     this.visible = true;
38637     this.collapsed = false;
38638     this.unrendered_panels = [];
38639 };
38640
38641 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38642
38643     position: '', // set by wrapper (eg. north/south etc..)
38644     unrendered_panels : null,  // unrendered panels.
38645     
38646     tabPosition : false,
38647     
38648     mgr: false, // points to 'Border'
38649     
38650     
38651     createBody : function(){
38652         /** This region's body element 
38653         * @type Roo.Element */
38654         this.bodyEl = this.el.createChild({
38655                 tag: "div",
38656                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38657         });
38658     },
38659
38660     onRender: function(ctr, pos)
38661     {
38662         var dh = Roo.DomHelper;
38663         /** This region's container element 
38664         * @type Roo.Element */
38665         this.el = dh.append(ctr.dom, {
38666                 tag: "div",
38667                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38668             }, true);
38669         /** This region's title element 
38670         * @type Roo.Element */
38671     
38672         this.titleEl = dh.append(this.el.dom,  {
38673                 tag: "div",
38674                 unselectable: "on",
38675                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38676                 children:[
38677                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38678                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38679                 ]
38680             }, true);
38681         
38682         this.titleEl.enableDisplayMode();
38683         /** This region's title text element 
38684         * @type HTMLElement */
38685         this.titleTextEl = this.titleEl.dom.firstChild;
38686         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38687         /*
38688         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38689         this.closeBtn.enableDisplayMode();
38690         this.closeBtn.on("click", this.closeClicked, this);
38691         this.closeBtn.hide();
38692     */
38693         this.createBody(this.config);
38694         if(this.config.hideWhenEmpty){
38695             this.hide();
38696             this.on("paneladded", this.validateVisibility, this);
38697             this.on("panelremoved", this.validateVisibility, this);
38698         }
38699         if(this.autoScroll){
38700             this.bodyEl.setStyle("overflow", "auto");
38701         }else{
38702             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38703         }
38704         //if(c.titlebar !== false){
38705             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38706                 this.titleEl.hide();
38707             }else{
38708                 this.titleEl.show();
38709                 if(this.config.title){
38710                     this.titleTextEl.innerHTML = this.config.title;
38711                 }
38712             }
38713         //}
38714         if(this.config.collapsed){
38715             this.collapse(true);
38716         }
38717         if(this.config.hidden){
38718             this.hide();
38719         }
38720         
38721         if (this.unrendered_panels && this.unrendered_panels.length) {
38722             for (var i =0;i< this.unrendered_panels.length; i++) {
38723                 this.add(this.unrendered_panels[i]);
38724             }
38725             this.unrendered_panels = null;
38726             
38727         }
38728         
38729     },
38730     
38731     applyConfig : function(c)
38732     {
38733         /*
38734          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38735             var dh = Roo.DomHelper;
38736             if(c.titlebar !== false){
38737                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38738                 this.collapseBtn.on("click", this.collapse, this);
38739                 this.collapseBtn.enableDisplayMode();
38740                 /*
38741                 if(c.showPin === true || this.showPin){
38742                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38743                     this.stickBtn.enableDisplayMode();
38744                     this.stickBtn.on("click", this.expand, this);
38745                     this.stickBtn.hide();
38746                 }
38747                 
38748             }
38749             */
38750             /** This region's collapsed element
38751             * @type Roo.Element */
38752             /*
38753              *
38754             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38755                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38756             ]}, true);
38757             
38758             if(c.floatable !== false){
38759                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38760                this.collapsedEl.on("click", this.collapseClick, this);
38761             }
38762
38763             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38764                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38765                    id: "message", unselectable: "on", style:{"float":"left"}});
38766                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38767              }
38768             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38769             this.expandBtn.on("click", this.expand, this);
38770             
38771         }
38772         
38773         if(this.collapseBtn){
38774             this.collapseBtn.setVisible(c.collapsible == true);
38775         }
38776         
38777         this.cmargins = c.cmargins || this.cmargins ||
38778                          (this.position == "west" || this.position == "east" ?
38779                              {top: 0, left: 2, right:2, bottom: 0} :
38780                              {top: 2, left: 0, right:0, bottom: 2});
38781         */
38782         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38783         
38784         
38785         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38786         
38787         this.autoScroll = c.autoScroll || false;
38788         
38789         
38790        
38791         
38792         this.duration = c.duration || .30;
38793         this.slideDuration = c.slideDuration || .45;
38794         this.config = c;
38795        
38796     },
38797     /**
38798      * Returns true if this region is currently visible.
38799      * @return {Boolean}
38800      */
38801     isVisible : function(){
38802         return this.visible;
38803     },
38804
38805     /**
38806      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38807      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38808      */
38809     //setCollapsedTitle : function(title){
38810     //    title = title || "&#160;";
38811      //   if(this.collapsedTitleTextEl){
38812       //      this.collapsedTitleTextEl.innerHTML = title;
38813        // }
38814     //},
38815
38816     getBox : function(){
38817         var b;
38818       //  if(!this.collapsed){
38819             b = this.el.getBox(false, true);
38820        // }else{
38821           //  b = this.collapsedEl.getBox(false, true);
38822         //}
38823         return b;
38824     },
38825
38826     getMargins : function(){
38827         return this.margins;
38828         //return this.collapsed ? this.cmargins : this.margins;
38829     },
38830 /*
38831     highlight : function(){
38832         this.el.addClass("x-layout-panel-dragover");
38833     },
38834
38835     unhighlight : function(){
38836         this.el.removeClass("x-layout-panel-dragover");
38837     },
38838 */
38839     updateBox : function(box)
38840     {
38841         if (!this.bodyEl) {
38842             return; // not rendered yet..
38843         }
38844         
38845         this.box = box;
38846         if(!this.collapsed){
38847             this.el.dom.style.left = box.x + "px";
38848             this.el.dom.style.top = box.y + "px";
38849             this.updateBody(box.width, box.height);
38850         }else{
38851             this.collapsedEl.dom.style.left = box.x + "px";
38852             this.collapsedEl.dom.style.top = box.y + "px";
38853             this.collapsedEl.setSize(box.width, box.height);
38854         }
38855         if(this.tabs){
38856             this.tabs.autoSizeTabs();
38857         }
38858     },
38859
38860     updateBody : function(w, h)
38861     {
38862         if(w !== null){
38863             this.el.setWidth(w);
38864             w -= this.el.getBorderWidth("rl");
38865             if(this.config.adjustments){
38866                 w += this.config.adjustments[0];
38867             }
38868         }
38869         if(h !== null && h > 0){
38870             this.el.setHeight(h);
38871             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38872             h -= this.el.getBorderWidth("tb");
38873             if(this.config.adjustments){
38874                 h += this.config.adjustments[1];
38875             }
38876             this.bodyEl.setHeight(h);
38877             if(this.tabs){
38878                 h = this.tabs.syncHeight(h);
38879             }
38880         }
38881         if(this.panelSize){
38882             w = w !== null ? w : this.panelSize.width;
38883             h = h !== null ? h : this.panelSize.height;
38884         }
38885         if(this.activePanel){
38886             var el = this.activePanel.getEl();
38887             w = w !== null ? w : el.getWidth();
38888             h = h !== null ? h : el.getHeight();
38889             this.panelSize = {width: w, height: h};
38890             this.activePanel.setSize(w, h);
38891         }
38892         if(Roo.isIE && this.tabs){
38893             this.tabs.el.repaint();
38894         }
38895     },
38896
38897     /**
38898      * Returns the container element for this region.
38899      * @return {Roo.Element}
38900      */
38901     getEl : function(){
38902         return this.el;
38903     },
38904
38905     /**
38906      * Hides this region.
38907      */
38908     hide : function(){
38909         //if(!this.collapsed){
38910             this.el.dom.style.left = "-2000px";
38911             this.el.hide();
38912         //}else{
38913          //   this.collapsedEl.dom.style.left = "-2000px";
38914          //   this.collapsedEl.hide();
38915        // }
38916         this.visible = false;
38917         this.fireEvent("visibilitychange", this, false);
38918     },
38919
38920     /**
38921      * Shows this region if it was previously hidden.
38922      */
38923     show : function(){
38924         //if(!this.collapsed){
38925             this.el.show();
38926         //}else{
38927         //    this.collapsedEl.show();
38928        // }
38929         this.visible = true;
38930         this.fireEvent("visibilitychange", this, true);
38931     },
38932 /*
38933     closeClicked : function(){
38934         if(this.activePanel){
38935             this.remove(this.activePanel);
38936         }
38937     },
38938
38939     collapseClick : function(e){
38940         if(this.isSlid){
38941            e.stopPropagation();
38942            this.slideIn();
38943         }else{
38944            e.stopPropagation();
38945            this.slideOut();
38946         }
38947     },
38948 */
38949     /**
38950      * Collapses this region.
38951      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38952      */
38953     /*
38954     collapse : function(skipAnim, skipCheck = false){
38955         if(this.collapsed) {
38956             return;
38957         }
38958         
38959         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38960             
38961             this.collapsed = true;
38962             if(this.split){
38963                 this.split.el.hide();
38964             }
38965             if(this.config.animate && skipAnim !== true){
38966                 this.fireEvent("invalidated", this);
38967                 this.animateCollapse();
38968             }else{
38969                 this.el.setLocation(-20000,-20000);
38970                 this.el.hide();
38971                 this.collapsedEl.show();
38972                 this.fireEvent("collapsed", this);
38973                 this.fireEvent("invalidated", this);
38974             }
38975         }
38976         
38977     },
38978 */
38979     animateCollapse : function(){
38980         // overridden
38981     },
38982
38983     /**
38984      * Expands this region if it was previously collapsed.
38985      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38986      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38987      */
38988     /*
38989     expand : function(e, skipAnim){
38990         if(e) {
38991             e.stopPropagation();
38992         }
38993         if(!this.collapsed || this.el.hasActiveFx()) {
38994             return;
38995         }
38996         if(this.isSlid){
38997             this.afterSlideIn();
38998             skipAnim = true;
38999         }
39000         this.collapsed = false;
39001         if(this.config.animate && skipAnim !== true){
39002             this.animateExpand();
39003         }else{
39004             this.el.show();
39005             if(this.split){
39006                 this.split.el.show();
39007             }
39008             this.collapsedEl.setLocation(-2000,-2000);
39009             this.collapsedEl.hide();
39010             this.fireEvent("invalidated", this);
39011             this.fireEvent("expanded", this);
39012         }
39013     },
39014 */
39015     animateExpand : function(){
39016         // overridden
39017     },
39018
39019     initTabs : function()
39020     {
39021         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39022         
39023         var ts = new Roo.bootstrap.panel.Tabs({
39024             el: this.bodyEl.dom,
39025             region : this,
39026             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39027             disableTooltips: this.config.disableTabTips,
39028             toolbar : this.config.toolbar
39029         });
39030         
39031         if(this.config.hideTabs){
39032             ts.stripWrap.setDisplayed(false);
39033         }
39034         this.tabs = ts;
39035         ts.resizeTabs = this.config.resizeTabs === true;
39036         ts.minTabWidth = this.config.minTabWidth || 40;
39037         ts.maxTabWidth = this.config.maxTabWidth || 250;
39038         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39039         ts.monitorResize = false;
39040         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39041         ts.bodyEl.addClass('roo-layout-tabs-body');
39042         this.panels.each(this.initPanelAsTab, this);
39043     },
39044
39045     initPanelAsTab : function(panel){
39046         var ti = this.tabs.addTab(
39047             panel.getEl().id,
39048             panel.getTitle(),
39049             null,
39050             this.config.closeOnTab && panel.isClosable(),
39051             panel.tpl
39052         );
39053         if(panel.tabTip !== undefined){
39054             ti.setTooltip(panel.tabTip);
39055         }
39056         ti.on("activate", function(){
39057               this.setActivePanel(panel);
39058         }, this);
39059         
39060         if(this.config.closeOnTab){
39061             ti.on("beforeclose", function(t, e){
39062                 e.cancel = true;
39063                 this.remove(panel);
39064             }, this);
39065         }
39066         
39067         panel.tabItem = ti;
39068         
39069         return ti;
39070     },
39071
39072     updatePanelTitle : function(panel, title)
39073     {
39074         if(this.activePanel == panel){
39075             this.updateTitle(title);
39076         }
39077         if(this.tabs){
39078             var ti = this.tabs.getTab(panel.getEl().id);
39079             ti.setText(title);
39080             if(panel.tabTip !== undefined){
39081                 ti.setTooltip(panel.tabTip);
39082             }
39083         }
39084     },
39085
39086     updateTitle : function(title){
39087         if(this.titleTextEl && !this.config.title){
39088             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39089         }
39090     },
39091
39092     setActivePanel : function(panel)
39093     {
39094         panel = this.getPanel(panel);
39095         if(this.activePanel && this.activePanel != panel){
39096             if(this.activePanel.setActiveState(false) === false){
39097                 return;
39098             }
39099         }
39100         this.activePanel = panel;
39101         panel.setActiveState(true);
39102         if(this.panelSize){
39103             panel.setSize(this.panelSize.width, this.panelSize.height);
39104         }
39105         if(this.closeBtn){
39106             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39107         }
39108         this.updateTitle(panel.getTitle());
39109         if(this.tabs){
39110             this.fireEvent("invalidated", this);
39111         }
39112         this.fireEvent("panelactivated", this, panel);
39113     },
39114
39115     /**
39116      * Shows the specified panel.
39117      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39118      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39119      */
39120     showPanel : function(panel)
39121     {
39122         panel = this.getPanel(panel);
39123         if(panel){
39124             if(this.tabs){
39125                 var tab = this.tabs.getTab(panel.getEl().id);
39126                 if(tab.isHidden()){
39127                     this.tabs.unhideTab(tab.id);
39128                 }
39129                 tab.activate();
39130             }else{
39131                 this.setActivePanel(panel);
39132             }
39133         }
39134         return panel;
39135     },
39136
39137     /**
39138      * Get the active panel for this region.
39139      * @return {Roo.ContentPanel} The active panel or null
39140      */
39141     getActivePanel : function(){
39142         return this.activePanel;
39143     },
39144
39145     validateVisibility : function(){
39146         if(this.panels.getCount() < 1){
39147             this.updateTitle("&#160;");
39148             this.closeBtn.hide();
39149             this.hide();
39150         }else{
39151             if(!this.isVisible()){
39152                 this.show();
39153             }
39154         }
39155     },
39156
39157     /**
39158      * Adds the passed ContentPanel(s) to this region.
39159      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39160      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39161      */
39162     add : function(panel)
39163     {
39164         if(arguments.length > 1){
39165             for(var i = 0, len = arguments.length; i < len; i++) {
39166                 this.add(arguments[i]);
39167             }
39168             return null;
39169         }
39170         
39171         // if we have not been rendered yet, then we can not really do much of this..
39172         if (!this.bodyEl) {
39173             this.unrendered_panels.push(panel);
39174             return panel;
39175         }
39176         
39177         
39178         
39179         
39180         if(this.hasPanel(panel)){
39181             this.showPanel(panel);
39182             return panel;
39183         }
39184         panel.setRegion(this);
39185         this.panels.add(panel);
39186        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39187             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39188             // and hide them... ???
39189             this.bodyEl.dom.appendChild(panel.getEl().dom);
39190             if(panel.background !== true){
39191                 this.setActivePanel(panel);
39192             }
39193             this.fireEvent("paneladded", this, panel);
39194             return panel;
39195         }
39196         */
39197         if(!this.tabs){
39198             this.initTabs();
39199         }else{
39200             this.initPanelAsTab(panel);
39201         }
39202         
39203         
39204         if(panel.background !== true){
39205             this.tabs.activate(panel.getEl().id);
39206         }
39207         this.fireEvent("paneladded", this, panel);
39208         return panel;
39209     },
39210
39211     /**
39212      * Hides the tab for the specified panel.
39213      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39214      */
39215     hidePanel : function(panel){
39216         if(this.tabs && (panel = this.getPanel(panel))){
39217             this.tabs.hideTab(panel.getEl().id);
39218         }
39219     },
39220
39221     /**
39222      * Unhides the tab for a previously hidden panel.
39223      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39224      */
39225     unhidePanel : function(panel){
39226         if(this.tabs && (panel = this.getPanel(panel))){
39227             this.tabs.unhideTab(panel.getEl().id);
39228         }
39229     },
39230
39231     clearPanels : function(){
39232         while(this.panels.getCount() > 0){
39233              this.remove(this.panels.first());
39234         }
39235     },
39236
39237     /**
39238      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39239      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39240      * @param {Boolean} preservePanel Overrides the config preservePanel option
39241      * @return {Roo.ContentPanel} The panel that was removed
39242      */
39243     remove : function(panel, preservePanel)
39244     {
39245         panel = this.getPanel(panel);
39246         if(!panel){
39247             return null;
39248         }
39249         var e = {};
39250         this.fireEvent("beforeremove", this, panel, e);
39251         if(e.cancel === true){
39252             return null;
39253         }
39254         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39255         var panelId = panel.getId();
39256         this.panels.removeKey(panelId);
39257         if(preservePanel){
39258             document.body.appendChild(panel.getEl().dom);
39259         }
39260         if(this.tabs){
39261             this.tabs.removeTab(panel.getEl().id);
39262         }else if (!preservePanel){
39263             this.bodyEl.dom.removeChild(panel.getEl().dom);
39264         }
39265         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39266             var p = this.panels.first();
39267             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39268             tempEl.appendChild(p.getEl().dom);
39269             this.bodyEl.update("");
39270             this.bodyEl.dom.appendChild(p.getEl().dom);
39271             tempEl = null;
39272             this.updateTitle(p.getTitle());
39273             this.tabs = null;
39274             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39275             this.setActivePanel(p);
39276         }
39277         panel.setRegion(null);
39278         if(this.activePanel == panel){
39279             this.activePanel = null;
39280         }
39281         if(this.config.autoDestroy !== false && preservePanel !== true){
39282             try{panel.destroy();}catch(e){}
39283         }
39284         this.fireEvent("panelremoved", this, panel);
39285         return panel;
39286     },
39287
39288     /**
39289      * Returns the TabPanel component used by this region
39290      * @return {Roo.TabPanel}
39291      */
39292     getTabs : function(){
39293         return this.tabs;
39294     },
39295
39296     createTool : function(parentEl, className){
39297         var btn = Roo.DomHelper.append(parentEl, {
39298             tag: "div",
39299             cls: "x-layout-tools-button",
39300             children: [ {
39301                 tag: "div",
39302                 cls: "roo-layout-tools-button-inner " + className,
39303                 html: "&#160;"
39304             }]
39305         }, true);
39306         btn.addClassOnOver("roo-layout-tools-button-over");
39307         return btn;
39308     }
39309 });/*
39310  * Based on:
39311  * Ext JS Library 1.1.1
39312  * Copyright(c) 2006-2007, Ext JS, LLC.
39313  *
39314  * Originally Released Under LGPL - original licence link has changed is not relivant.
39315  *
39316  * Fork - LGPL
39317  * <script type="text/javascript">
39318  */
39319  
39320
39321
39322 /**
39323  * @class Roo.SplitLayoutRegion
39324  * @extends Roo.LayoutRegion
39325  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39326  */
39327 Roo.bootstrap.layout.Split = function(config){
39328     this.cursor = config.cursor;
39329     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39330 };
39331
39332 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39333 {
39334     splitTip : "Drag to resize.",
39335     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39336     useSplitTips : false,
39337
39338     applyConfig : function(config){
39339         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39340     },
39341     
39342     onRender : function(ctr,pos) {
39343         
39344         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39345         if(!this.config.split){
39346             return;
39347         }
39348         if(!this.split){
39349             
39350             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39351                             tag: "div",
39352                             id: this.el.id + "-split",
39353                             cls: "roo-layout-split roo-layout-split-"+this.position,
39354                             html: "&#160;"
39355             });
39356             /** The SplitBar for this region 
39357             * @type Roo.SplitBar */
39358             // does not exist yet...
39359             Roo.log([this.position, this.orientation]);
39360             
39361             this.split = new Roo.bootstrap.SplitBar({
39362                 dragElement : splitEl,
39363                 resizingElement: this.el,
39364                 orientation : this.orientation
39365             });
39366             
39367             this.split.on("moved", this.onSplitMove, this);
39368             this.split.useShim = this.config.useShim === true;
39369             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39370             if(this.useSplitTips){
39371                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39372             }
39373             //if(config.collapsible){
39374             //    this.split.el.on("dblclick", this.collapse,  this);
39375             //}
39376         }
39377         if(typeof this.config.minSize != "undefined"){
39378             this.split.minSize = this.config.minSize;
39379         }
39380         if(typeof this.config.maxSize != "undefined"){
39381             this.split.maxSize = this.config.maxSize;
39382         }
39383         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39384             this.hideSplitter();
39385         }
39386         
39387     },
39388
39389     getHMaxSize : function(){
39390          var cmax = this.config.maxSize || 10000;
39391          var center = this.mgr.getRegion("center");
39392          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39393     },
39394
39395     getVMaxSize : function(){
39396          var cmax = this.config.maxSize || 10000;
39397          var center = this.mgr.getRegion("center");
39398          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39399     },
39400
39401     onSplitMove : function(split, newSize){
39402         this.fireEvent("resized", this, newSize);
39403     },
39404     
39405     /** 
39406      * Returns the {@link Roo.SplitBar} for this region.
39407      * @return {Roo.SplitBar}
39408      */
39409     getSplitBar : function(){
39410         return this.split;
39411     },
39412     
39413     hide : function(){
39414         this.hideSplitter();
39415         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39416     },
39417
39418     hideSplitter : function(){
39419         if(this.split){
39420             this.split.el.setLocation(-2000,-2000);
39421             this.split.el.hide();
39422         }
39423     },
39424
39425     show : function(){
39426         if(this.split){
39427             this.split.el.show();
39428         }
39429         Roo.bootstrap.layout.Split.superclass.show.call(this);
39430     },
39431     
39432     beforeSlide: function(){
39433         if(Roo.isGecko){// firefox overflow auto bug workaround
39434             this.bodyEl.clip();
39435             if(this.tabs) {
39436                 this.tabs.bodyEl.clip();
39437             }
39438             if(this.activePanel){
39439                 this.activePanel.getEl().clip();
39440                 
39441                 if(this.activePanel.beforeSlide){
39442                     this.activePanel.beforeSlide();
39443                 }
39444             }
39445         }
39446     },
39447     
39448     afterSlide : function(){
39449         if(Roo.isGecko){// firefox overflow auto bug workaround
39450             this.bodyEl.unclip();
39451             if(this.tabs) {
39452                 this.tabs.bodyEl.unclip();
39453             }
39454             if(this.activePanel){
39455                 this.activePanel.getEl().unclip();
39456                 if(this.activePanel.afterSlide){
39457                     this.activePanel.afterSlide();
39458                 }
39459             }
39460         }
39461     },
39462
39463     initAutoHide : function(){
39464         if(this.autoHide !== false){
39465             if(!this.autoHideHd){
39466                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39467                 this.autoHideHd = {
39468                     "mouseout": function(e){
39469                         if(!e.within(this.el, true)){
39470                             st.delay(500);
39471                         }
39472                     },
39473                     "mouseover" : function(e){
39474                         st.cancel();
39475                     },
39476                     scope : this
39477                 };
39478             }
39479             this.el.on(this.autoHideHd);
39480         }
39481     },
39482
39483     clearAutoHide : function(){
39484         if(this.autoHide !== false){
39485             this.el.un("mouseout", this.autoHideHd.mouseout);
39486             this.el.un("mouseover", this.autoHideHd.mouseover);
39487         }
39488     },
39489
39490     clearMonitor : function(){
39491         Roo.get(document).un("click", this.slideInIf, this);
39492     },
39493
39494     // these names are backwards but not changed for compat
39495     slideOut : function(){
39496         if(this.isSlid || this.el.hasActiveFx()){
39497             return;
39498         }
39499         this.isSlid = true;
39500         if(this.collapseBtn){
39501             this.collapseBtn.hide();
39502         }
39503         this.closeBtnState = this.closeBtn.getStyle('display');
39504         this.closeBtn.hide();
39505         if(this.stickBtn){
39506             this.stickBtn.show();
39507         }
39508         this.el.show();
39509         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39510         this.beforeSlide();
39511         this.el.setStyle("z-index", 10001);
39512         this.el.slideIn(this.getSlideAnchor(), {
39513             callback: function(){
39514                 this.afterSlide();
39515                 this.initAutoHide();
39516                 Roo.get(document).on("click", this.slideInIf, this);
39517                 this.fireEvent("slideshow", this);
39518             },
39519             scope: this,
39520             block: true
39521         });
39522     },
39523
39524     afterSlideIn : function(){
39525         this.clearAutoHide();
39526         this.isSlid = false;
39527         this.clearMonitor();
39528         this.el.setStyle("z-index", "");
39529         if(this.collapseBtn){
39530             this.collapseBtn.show();
39531         }
39532         this.closeBtn.setStyle('display', this.closeBtnState);
39533         if(this.stickBtn){
39534             this.stickBtn.hide();
39535         }
39536         this.fireEvent("slidehide", this);
39537     },
39538
39539     slideIn : function(cb){
39540         if(!this.isSlid || this.el.hasActiveFx()){
39541             Roo.callback(cb);
39542             return;
39543         }
39544         this.isSlid = false;
39545         this.beforeSlide();
39546         this.el.slideOut(this.getSlideAnchor(), {
39547             callback: function(){
39548                 this.el.setLeftTop(-10000, -10000);
39549                 this.afterSlide();
39550                 this.afterSlideIn();
39551                 Roo.callback(cb);
39552             },
39553             scope: this,
39554             block: true
39555         });
39556     },
39557     
39558     slideInIf : function(e){
39559         if(!e.within(this.el)){
39560             this.slideIn();
39561         }
39562     },
39563
39564     animateCollapse : function(){
39565         this.beforeSlide();
39566         this.el.setStyle("z-index", 20000);
39567         var anchor = this.getSlideAnchor();
39568         this.el.slideOut(anchor, {
39569             callback : function(){
39570                 this.el.setStyle("z-index", "");
39571                 this.collapsedEl.slideIn(anchor, {duration:.3});
39572                 this.afterSlide();
39573                 this.el.setLocation(-10000,-10000);
39574                 this.el.hide();
39575                 this.fireEvent("collapsed", this);
39576             },
39577             scope: this,
39578             block: true
39579         });
39580     },
39581
39582     animateExpand : function(){
39583         this.beforeSlide();
39584         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39585         this.el.setStyle("z-index", 20000);
39586         this.collapsedEl.hide({
39587             duration:.1
39588         });
39589         this.el.slideIn(this.getSlideAnchor(), {
39590             callback : function(){
39591                 this.el.setStyle("z-index", "");
39592                 this.afterSlide();
39593                 if(this.split){
39594                     this.split.el.show();
39595                 }
39596                 this.fireEvent("invalidated", this);
39597                 this.fireEvent("expanded", this);
39598             },
39599             scope: this,
39600             block: true
39601         });
39602     },
39603
39604     anchors : {
39605         "west" : "left",
39606         "east" : "right",
39607         "north" : "top",
39608         "south" : "bottom"
39609     },
39610
39611     sanchors : {
39612         "west" : "l",
39613         "east" : "r",
39614         "north" : "t",
39615         "south" : "b"
39616     },
39617
39618     canchors : {
39619         "west" : "tl-tr",
39620         "east" : "tr-tl",
39621         "north" : "tl-bl",
39622         "south" : "bl-tl"
39623     },
39624
39625     getAnchor : function(){
39626         return this.anchors[this.position];
39627     },
39628
39629     getCollapseAnchor : function(){
39630         return this.canchors[this.position];
39631     },
39632
39633     getSlideAnchor : function(){
39634         return this.sanchors[this.position];
39635     },
39636
39637     getAlignAdj : function(){
39638         var cm = this.cmargins;
39639         switch(this.position){
39640             case "west":
39641                 return [0, 0];
39642             break;
39643             case "east":
39644                 return [0, 0];
39645             break;
39646             case "north":
39647                 return [0, 0];
39648             break;
39649             case "south":
39650                 return [0, 0];
39651             break;
39652         }
39653     },
39654
39655     getExpandAdj : function(){
39656         var c = this.collapsedEl, cm = this.cmargins;
39657         switch(this.position){
39658             case "west":
39659                 return [-(cm.right+c.getWidth()+cm.left), 0];
39660             break;
39661             case "east":
39662                 return [cm.right+c.getWidth()+cm.left, 0];
39663             break;
39664             case "north":
39665                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39666             break;
39667             case "south":
39668                 return [0, cm.top+cm.bottom+c.getHeight()];
39669             break;
39670         }
39671     }
39672 });/*
39673  * Based on:
39674  * Ext JS Library 1.1.1
39675  * Copyright(c) 2006-2007, Ext JS, LLC.
39676  *
39677  * Originally Released Under LGPL - original licence link has changed is not relivant.
39678  *
39679  * Fork - LGPL
39680  * <script type="text/javascript">
39681  */
39682 /*
39683  * These classes are private internal classes
39684  */
39685 Roo.bootstrap.layout.Center = function(config){
39686     config.region = "center";
39687     Roo.bootstrap.layout.Region.call(this, config);
39688     this.visible = true;
39689     this.minWidth = config.minWidth || 20;
39690     this.minHeight = config.minHeight || 20;
39691 };
39692
39693 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39694     hide : function(){
39695         // center panel can't be hidden
39696     },
39697     
39698     show : function(){
39699         // center panel can't be hidden
39700     },
39701     
39702     getMinWidth: function(){
39703         return this.minWidth;
39704     },
39705     
39706     getMinHeight: function(){
39707         return this.minHeight;
39708     }
39709 });
39710
39711
39712
39713
39714  
39715
39716
39717
39718
39719
39720
39721 Roo.bootstrap.layout.North = function(config)
39722 {
39723     config.region = 'north';
39724     config.cursor = 'n-resize';
39725     
39726     Roo.bootstrap.layout.Split.call(this, config);
39727     
39728     
39729     if(this.split){
39730         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39731         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39732         this.split.el.addClass("roo-layout-split-v");
39733     }
39734     //var size = config.initialSize || config.height;
39735     //if(this.el && typeof size != "undefined"){
39736     //    this.el.setHeight(size);
39737     //}
39738 };
39739 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39740 {
39741     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39742      
39743      
39744     onRender : function(ctr, pos)
39745     {
39746         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39747         var size = this.config.initialSize || this.config.height;
39748         if(this.el && typeof size != "undefined"){
39749             this.el.setHeight(size);
39750         }
39751     
39752     },
39753     
39754     getBox : function(){
39755         if(this.collapsed){
39756             return this.collapsedEl.getBox();
39757         }
39758         var box = this.el.getBox();
39759         if(this.split){
39760             box.height += this.split.el.getHeight();
39761         }
39762         return box;
39763     },
39764     
39765     updateBox : function(box){
39766         if(this.split && !this.collapsed){
39767             box.height -= this.split.el.getHeight();
39768             this.split.el.setLeft(box.x);
39769             this.split.el.setTop(box.y+box.height);
39770             this.split.el.setWidth(box.width);
39771         }
39772         if(this.collapsed){
39773             this.updateBody(box.width, null);
39774         }
39775         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39776     }
39777 });
39778
39779
39780
39781
39782
39783 Roo.bootstrap.layout.South = function(config){
39784     config.region = 'south';
39785     config.cursor = 's-resize';
39786     Roo.bootstrap.layout.Split.call(this, config);
39787     if(this.split){
39788         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39789         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39790         this.split.el.addClass("roo-layout-split-v");
39791     }
39792     
39793 };
39794
39795 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39796     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39797     
39798     onRender : function(ctr, pos)
39799     {
39800         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39801         var size = this.config.initialSize || this.config.height;
39802         if(this.el && typeof size != "undefined"){
39803             this.el.setHeight(size);
39804         }
39805     
39806     },
39807     
39808     getBox : function(){
39809         if(this.collapsed){
39810             return this.collapsedEl.getBox();
39811         }
39812         var box = this.el.getBox();
39813         if(this.split){
39814             var sh = this.split.el.getHeight();
39815             box.height += sh;
39816             box.y -= sh;
39817         }
39818         return box;
39819     },
39820     
39821     updateBox : function(box){
39822         if(this.split && !this.collapsed){
39823             var sh = this.split.el.getHeight();
39824             box.height -= sh;
39825             box.y += sh;
39826             this.split.el.setLeft(box.x);
39827             this.split.el.setTop(box.y-sh);
39828             this.split.el.setWidth(box.width);
39829         }
39830         if(this.collapsed){
39831             this.updateBody(box.width, null);
39832         }
39833         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39834     }
39835 });
39836
39837 Roo.bootstrap.layout.East = function(config){
39838     config.region = "east";
39839     config.cursor = "e-resize";
39840     Roo.bootstrap.layout.Split.call(this, config);
39841     if(this.split){
39842         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39843         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39844         this.split.el.addClass("roo-layout-split-h");
39845     }
39846     
39847 };
39848 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39849     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39850     
39851     onRender : function(ctr, pos)
39852     {
39853         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39854         var size = this.config.initialSize || this.config.width;
39855         if(this.el && typeof size != "undefined"){
39856             this.el.setWidth(size);
39857         }
39858     
39859     },
39860     
39861     getBox : function(){
39862         if(this.collapsed){
39863             return this.collapsedEl.getBox();
39864         }
39865         var box = this.el.getBox();
39866         if(this.split){
39867             var sw = this.split.el.getWidth();
39868             box.width += sw;
39869             box.x -= sw;
39870         }
39871         return box;
39872     },
39873
39874     updateBox : function(box){
39875         if(this.split && !this.collapsed){
39876             var sw = this.split.el.getWidth();
39877             box.width -= sw;
39878             this.split.el.setLeft(box.x);
39879             this.split.el.setTop(box.y);
39880             this.split.el.setHeight(box.height);
39881             box.x += sw;
39882         }
39883         if(this.collapsed){
39884             this.updateBody(null, box.height);
39885         }
39886         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39887     }
39888 });
39889
39890 Roo.bootstrap.layout.West = function(config){
39891     config.region = "west";
39892     config.cursor = "w-resize";
39893     
39894     Roo.bootstrap.layout.Split.call(this, config);
39895     if(this.split){
39896         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39897         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39898         this.split.el.addClass("roo-layout-split-h");
39899     }
39900     
39901 };
39902 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39903     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39904     
39905     onRender: function(ctr, pos)
39906     {
39907         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39908         var size = this.config.initialSize || this.config.width;
39909         if(typeof size != "undefined"){
39910             this.el.setWidth(size);
39911         }
39912     },
39913     
39914     getBox : function(){
39915         if(this.collapsed){
39916             return this.collapsedEl.getBox();
39917         }
39918         var box = this.el.getBox();
39919         if (box.width == 0) {
39920             box.width = this.config.width; // kludge?
39921         }
39922         if(this.split){
39923             box.width += this.split.el.getWidth();
39924         }
39925         return box;
39926     },
39927     
39928     updateBox : function(box){
39929         if(this.split && !this.collapsed){
39930             var sw = this.split.el.getWidth();
39931             box.width -= sw;
39932             this.split.el.setLeft(box.x+box.width);
39933             this.split.el.setTop(box.y);
39934             this.split.el.setHeight(box.height);
39935         }
39936         if(this.collapsed){
39937             this.updateBody(null, box.height);
39938         }
39939         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39940     }
39941 });Roo.namespace("Roo.bootstrap.panel");/*
39942  * Based on:
39943  * Ext JS Library 1.1.1
39944  * Copyright(c) 2006-2007, Ext JS, LLC.
39945  *
39946  * Originally Released Under LGPL - original licence link has changed is not relivant.
39947  *
39948  * Fork - LGPL
39949  * <script type="text/javascript">
39950  */
39951 /**
39952  * @class Roo.ContentPanel
39953  * @extends Roo.util.Observable
39954  * A basic ContentPanel element.
39955  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39956  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39957  * @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
39958  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39959  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39960  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39961  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39962  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39963  * @cfg {String} title          The title for this panel
39964  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39965  * @cfg {String} url            Calls {@link #setUrl} with this value
39966  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39967  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39968  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39969  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39970  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39971  * @cfg {Boolean} badges render the badges
39972  * @cfg {String} cls  extra classes to use  
39973  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39974
39975  * @constructor
39976  * Create a new ContentPanel.
39977  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39978  * @param {String/Object} config A string to set only the title or a config object
39979  * @param {String} content (optional) Set the HTML content for this panel
39980  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39981  */
39982 Roo.bootstrap.panel.Content = function( config){
39983     
39984     this.tpl = config.tpl || false;
39985     
39986     var el = config.el;
39987     var content = config.content;
39988
39989     if(config.autoCreate){ // xtype is available if this is called from factory
39990         el = Roo.id();
39991     }
39992     this.el = Roo.get(el);
39993     if(!this.el && config && config.autoCreate){
39994         if(typeof config.autoCreate == "object"){
39995             if(!config.autoCreate.id){
39996                 config.autoCreate.id = config.id||el;
39997             }
39998             this.el = Roo.DomHelper.append(document.body,
39999                         config.autoCreate, true);
40000         }else{
40001             var elcfg =  {
40002                 tag: "div",
40003                 cls: (config.cls || '') +
40004                     (config.background ? ' bg-' + config.background : '') +
40005                     " roo-layout-inactive-content",
40006                 id: config.id||el
40007             };
40008             if (config.iframe) {
40009                 elcfg.cn = [
40010                     {
40011                         tag : 'iframe',
40012                         style : 'border: 0px',
40013                         src : 'about:blank'
40014                     }
40015                 ];
40016             }
40017               
40018             if (config.html) {
40019                 elcfg.html = config.html;
40020                 
40021             }
40022                         
40023             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40024             if (config.iframe) {
40025                 this.iframeEl = this.el.select('iframe',true).first();
40026             }
40027             
40028         }
40029     } 
40030     this.closable = false;
40031     this.loaded = false;
40032     this.active = false;
40033    
40034       
40035     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40036         
40037         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40038         
40039         this.wrapEl = this.el; //this.el.wrap();
40040         var ti = [];
40041         if (config.toolbar.items) {
40042             ti = config.toolbar.items ;
40043             delete config.toolbar.items ;
40044         }
40045         
40046         var nitems = [];
40047         this.toolbar.render(this.wrapEl, 'before');
40048         for(var i =0;i < ti.length;i++) {
40049           //  Roo.log(['add child', items[i]]);
40050             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40051         }
40052         this.toolbar.items = nitems;
40053         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40054         delete config.toolbar;
40055         
40056     }
40057     /*
40058     // xtype created footer. - not sure if will work as we normally have to render first..
40059     if (this.footer && !this.footer.el && this.footer.xtype) {
40060         if (!this.wrapEl) {
40061             this.wrapEl = this.el.wrap();
40062         }
40063     
40064         this.footer.container = this.wrapEl.createChild();
40065          
40066         this.footer = Roo.factory(this.footer, Roo);
40067         
40068     }
40069     */
40070     
40071      if(typeof config == "string"){
40072         this.title = config;
40073     }else{
40074         Roo.apply(this, config);
40075     }
40076     
40077     if(this.resizeEl){
40078         this.resizeEl = Roo.get(this.resizeEl, true);
40079     }else{
40080         this.resizeEl = this.el;
40081     }
40082     // handle view.xtype
40083     
40084  
40085     
40086     
40087     this.addEvents({
40088         /**
40089          * @event activate
40090          * Fires when this panel is activated. 
40091          * @param {Roo.ContentPanel} this
40092          */
40093         "activate" : true,
40094         /**
40095          * @event deactivate
40096          * Fires when this panel is activated. 
40097          * @param {Roo.ContentPanel} this
40098          */
40099         "deactivate" : true,
40100
40101         /**
40102          * @event resize
40103          * Fires when this panel is resized if fitToFrame is true.
40104          * @param {Roo.ContentPanel} this
40105          * @param {Number} width The width after any component adjustments
40106          * @param {Number} height The height after any component adjustments
40107          */
40108         "resize" : true,
40109         
40110          /**
40111          * @event render
40112          * Fires when this tab is created
40113          * @param {Roo.ContentPanel} this
40114          */
40115         "render" : true
40116         
40117         
40118         
40119     });
40120     
40121
40122     
40123     
40124     if(this.autoScroll && !this.iframe){
40125         this.resizeEl.setStyle("overflow", "auto");
40126     } else {
40127         // fix randome scrolling
40128         //this.el.on('scroll', function() {
40129         //    Roo.log('fix random scolling');
40130         //    this.scrollTo('top',0); 
40131         //});
40132     }
40133     content = content || this.content;
40134     if(content){
40135         this.setContent(content);
40136     }
40137     if(config && config.url){
40138         this.setUrl(this.url, this.params, this.loadOnce);
40139     }
40140     
40141     
40142     
40143     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40144     
40145     if (this.view && typeof(this.view.xtype) != 'undefined') {
40146         this.view.el = this.el.appendChild(document.createElement("div"));
40147         this.view = Roo.factory(this.view); 
40148         this.view.render  &&  this.view.render(false, '');  
40149     }
40150     
40151     
40152     this.fireEvent('render', this);
40153 };
40154
40155 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40156     
40157     cls : '',
40158     background : '',
40159     
40160     tabTip : '',
40161     
40162     iframe : false,
40163     iframeEl : false,
40164     
40165     setRegion : function(region){
40166         this.region = region;
40167         this.setActiveClass(region && !this.background);
40168     },
40169     
40170     
40171     setActiveClass: function(state)
40172     {
40173         if(state){
40174            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40175            this.el.setStyle('position','relative');
40176         }else{
40177            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40178            this.el.setStyle('position', 'absolute');
40179         } 
40180     },
40181     
40182     /**
40183      * Returns the toolbar for this Panel if one was configured. 
40184      * @return {Roo.Toolbar} 
40185      */
40186     getToolbar : function(){
40187         return this.toolbar;
40188     },
40189     
40190     setActiveState : function(active)
40191     {
40192         this.active = active;
40193         this.setActiveClass(active);
40194         if(!active){
40195             if(this.fireEvent("deactivate", this) === false){
40196                 return false;
40197             }
40198             return true;
40199         }
40200         this.fireEvent("activate", this);
40201         return true;
40202     },
40203     /**
40204      * Updates this panel's element (not for iframe)
40205      * @param {String} content The new content
40206      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40207     */
40208     setContent : function(content, loadScripts){
40209         if (this.iframe) {
40210             return;
40211         }
40212         
40213         this.el.update(content, loadScripts);
40214     },
40215
40216     ignoreResize : function(w, h){
40217         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40218             return true;
40219         }else{
40220             this.lastSize = {width: w, height: h};
40221             return false;
40222         }
40223     },
40224     /**
40225      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40226      * @return {Roo.UpdateManager} The UpdateManager
40227      */
40228     getUpdateManager : function(){
40229         if (this.iframe) {
40230             return false;
40231         }
40232         return this.el.getUpdateManager();
40233     },
40234      /**
40235      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40236      * Does not work with IFRAME contents
40237      * @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:
40238 <pre><code>
40239 panel.load({
40240     url: "your-url.php",
40241     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40242     callback: yourFunction,
40243     scope: yourObject, //(optional scope)
40244     discardUrl: false,
40245     nocache: false,
40246     text: "Loading...",
40247     timeout: 30,
40248     scripts: false
40249 });
40250 </code></pre>
40251      
40252      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40253      * 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.
40254      * @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}
40255      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40256      * @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.
40257      * @return {Roo.ContentPanel} this
40258      */
40259     load : function(){
40260         
40261         if (this.iframe) {
40262             return this;
40263         }
40264         
40265         var um = this.el.getUpdateManager();
40266         um.update.apply(um, arguments);
40267         return this;
40268     },
40269
40270
40271     /**
40272      * 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.
40273      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40274      * @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)
40275      * @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)
40276      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40277      */
40278     setUrl : function(url, params, loadOnce){
40279         if (this.iframe) {
40280             this.iframeEl.dom.src = url;
40281             return false;
40282         }
40283         
40284         if(this.refreshDelegate){
40285             this.removeListener("activate", this.refreshDelegate);
40286         }
40287         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40288         this.on("activate", this.refreshDelegate);
40289         return this.el.getUpdateManager();
40290     },
40291     
40292     _handleRefresh : function(url, params, loadOnce){
40293         if(!loadOnce || !this.loaded){
40294             var updater = this.el.getUpdateManager();
40295             updater.update(url, params, this._setLoaded.createDelegate(this));
40296         }
40297     },
40298     
40299     _setLoaded : function(){
40300         this.loaded = true;
40301     }, 
40302     
40303     /**
40304      * Returns this panel's id
40305      * @return {String} 
40306      */
40307     getId : function(){
40308         return this.el.id;
40309     },
40310     
40311     /** 
40312      * Returns this panel's element - used by regiosn to add.
40313      * @return {Roo.Element} 
40314      */
40315     getEl : function(){
40316         return this.wrapEl || this.el;
40317     },
40318     
40319    
40320     
40321     adjustForComponents : function(width, height)
40322     {
40323         //Roo.log('adjustForComponents ');
40324         if(this.resizeEl != this.el){
40325             width -= this.el.getFrameWidth('lr');
40326             height -= this.el.getFrameWidth('tb');
40327         }
40328         if(this.toolbar){
40329             var te = this.toolbar.getEl();
40330             te.setWidth(width);
40331             height -= te.getHeight();
40332         }
40333         if(this.footer){
40334             var te = this.footer.getEl();
40335             te.setWidth(width);
40336             height -= te.getHeight();
40337         }
40338         
40339         
40340         if(this.adjustments){
40341             width += this.adjustments[0];
40342             height += this.adjustments[1];
40343         }
40344         return {"width": width, "height": height};
40345     },
40346     
40347     setSize : function(width, height){
40348         if(this.fitToFrame && !this.ignoreResize(width, height)){
40349             if(this.fitContainer && this.resizeEl != this.el){
40350                 this.el.setSize(width, height);
40351             }
40352             var size = this.adjustForComponents(width, height);
40353             if (this.iframe) {
40354                 this.iframeEl.setSize(width,height);
40355             }
40356             
40357             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40358             this.fireEvent('resize', this, size.width, size.height);
40359             
40360             
40361         }
40362     },
40363     
40364     /**
40365      * Returns this panel's title
40366      * @return {String} 
40367      */
40368     getTitle : function(){
40369         
40370         if (typeof(this.title) != 'object') {
40371             return this.title;
40372         }
40373         
40374         var t = '';
40375         for (var k in this.title) {
40376             if (!this.title.hasOwnProperty(k)) {
40377                 continue;
40378             }
40379             
40380             if (k.indexOf('-') >= 0) {
40381                 var s = k.split('-');
40382                 for (var i = 0; i<s.length; i++) {
40383                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40384                 }
40385             } else {
40386                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40387             }
40388         }
40389         return t;
40390     },
40391     
40392     /**
40393      * Set this panel's title
40394      * @param {String} title
40395      */
40396     setTitle : function(title){
40397         this.title = title;
40398         if(this.region){
40399             this.region.updatePanelTitle(this, title);
40400         }
40401     },
40402     
40403     /**
40404      * Returns true is this panel was configured to be closable
40405      * @return {Boolean} 
40406      */
40407     isClosable : function(){
40408         return this.closable;
40409     },
40410     
40411     beforeSlide : function(){
40412         this.el.clip();
40413         this.resizeEl.clip();
40414     },
40415     
40416     afterSlide : function(){
40417         this.el.unclip();
40418         this.resizeEl.unclip();
40419     },
40420     
40421     /**
40422      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40423      *   Will fail silently if the {@link #setUrl} method has not been called.
40424      *   This does not activate the panel, just updates its content.
40425      */
40426     refresh : function(){
40427         if(this.refreshDelegate){
40428            this.loaded = false;
40429            this.refreshDelegate();
40430         }
40431     },
40432     
40433     /**
40434      * Destroys this panel
40435      */
40436     destroy : function(){
40437         this.el.removeAllListeners();
40438         var tempEl = document.createElement("span");
40439         tempEl.appendChild(this.el.dom);
40440         tempEl.innerHTML = "";
40441         this.el.remove();
40442         this.el = null;
40443     },
40444     
40445     /**
40446      * form - if the content panel contains a form - this is a reference to it.
40447      * @type {Roo.form.Form}
40448      */
40449     form : false,
40450     /**
40451      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40452      *    This contains a reference to it.
40453      * @type {Roo.View}
40454      */
40455     view : false,
40456     
40457       /**
40458      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40459      * <pre><code>
40460
40461 layout.addxtype({
40462        xtype : 'Form',
40463        items: [ .... ]
40464    }
40465 );
40466
40467 </code></pre>
40468      * @param {Object} cfg Xtype definition of item to add.
40469      */
40470     
40471     
40472     getChildContainer: function () {
40473         return this.getEl();
40474     }
40475     
40476     
40477     /*
40478         var  ret = new Roo.factory(cfg);
40479         return ret;
40480         
40481         
40482         // add form..
40483         if (cfg.xtype.match(/^Form$/)) {
40484             
40485             var el;
40486             //if (this.footer) {
40487             //    el = this.footer.container.insertSibling(false, 'before');
40488             //} else {
40489                 el = this.el.createChild();
40490             //}
40491
40492             this.form = new  Roo.form.Form(cfg);
40493             
40494             
40495             if ( this.form.allItems.length) {
40496                 this.form.render(el.dom);
40497             }
40498             return this.form;
40499         }
40500         // should only have one of theses..
40501         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40502             // views.. should not be just added - used named prop 'view''
40503             
40504             cfg.el = this.el.appendChild(document.createElement("div"));
40505             // factory?
40506             
40507             var ret = new Roo.factory(cfg);
40508              
40509              ret.render && ret.render(false, ''); // render blank..
40510             this.view = ret;
40511             return ret;
40512         }
40513         return false;
40514     }
40515     \*/
40516 });
40517  
40518 /**
40519  * @class Roo.bootstrap.panel.Grid
40520  * @extends Roo.bootstrap.panel.Content
40521  * @constructor
40522  * Create a new GridPanel.
40523  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40524  * @param {Object} config A the config object
40525   
40526  */
40527
40528
40529
40530 Roo.bootstrap.panel.Grid = function(config)
40531 {
40532     
40533       
40534     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40535         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40536
40537     config.el = this.wrapper;
40538     //this.el = this.wrapper;
40539     
40540       if (config.container) {
40541         // ctor'ed from a Border/panel.grid
40542         
40543         
40544         this.wrapper.setStyle("overflow", "hidden");
40545         this.wrapper.addClass('roo-grid-container');
40546
40547     }
40548     
40549     
40550     if(config.toolbar){
40551         var tool_el = this.wrapper.createChild();    
40552         this.toolbar = Roo.factory(config.toolbar);
40553         var ti = [];
40554         if (config.toolbar.items) {
40555             ti = config.toolbar.items ;
40556             delete config.toolbar.items ;
40557         }
40558         
40559         var nitems = [];
40560         this.toolbar.render(tool_el);
40561         for(var i =0;i < ti.length;i++) {
40562           //  Roo.log(['add child', items[i]]);
40563             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40564         }
40565         this.toolbar.items = nitems;
40566         
40567         delete config.toolbar;
40568     }
40569     
40570     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40571     config.grid.scrollBody = true;;
40572     config.grid.monitorWindowResize = false; // turn off autosizing
40573     config.grid.autoHeight = false;
40574     config.grid.autoWidth = false;
40575     
40576     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40577     
40578     if (config.background) {
40579         // render grid on panel activation (if panel background)
40580         this.on('activate', function(gp) {
40581             if (!gp.grid.rendered) {
40582                 gp.grid.render(this.wrapper);
40583                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40584             }
40585         });
40586             
40587     } else {
40588         this.grid.render(this.wrapper);
40589         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40590
40591     }
40592     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40593     // ??? needed ??? config.el = this.wrapper;
40594     
40595     
40596     
40597   
40598     // xtype created footer. - not sure if will work as we normally have to render first..
40599     if (this.footer && !this.footer.el && this.footer.xtype) {
40600         
40601         var ctr = this.grid.getView().getFooterPanel(true);
40602         this.footer.dataSource = this.grid.dataSource;
40603         this.footer = Roo.factory(this.footer, Roo);
40604         this.footer.render(ctr);
40605         
40606     }
40607     
40608     
40609     
40610     
40611      
40612 };
40613
40614 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40615     getId : function(){
40616         return this.grid.id;
40617     },
40618     
40619     /**
40620      * Returns the grid for this panel
40621      * @return {Roo.bootstrap.Table} 
40622      */
40623     getGrid : function(){
40624         return this.grid;    
40625     },
40626     
40627     setSize : function(width, height){
40628         if(!this.ignoreResize(width, height)){
40629             var grid = this.grid;
40630             var size = this.adjustForComponents(width, height);
40631             // tfoot is not a footer?
40632           
40633             
40634             var gridel = grid.getGridEl();
40635             gridel.setSize(size.width, size.height);
40636             
40637             var tbd = grid.getGridEl().select('tbody', true).first();
40638             var thd = grid.getGridEl().select('thead',true).first();
40639             var tbf= grid.getGridEl().select('tfoot', true).first();
40640
40641             if (tbf) {
40642                 size.height -= tbf.getHeight();
40643             }
40644             if (thd) {
40645                 size.height -= thd.getHeight();
40646             }
40647             
40648             tbd.setSize(size.width, size.height );
40649             // this is for the account management tab -seems to work there.
40650             var thd = grid.getGridEl().select('thead',true).first();
40651             //if (tbd) {
40652             //    tbd.setSize(size.width, size.height - thd.getHeight());
40653             //}
40654              
40655             grid.autoSize();
40656         }
40657     },
40658      
40659     
40660     
40661     beforeSlide : function(){
40662         this.grid.getView().scroller.clip();
40663     },
40664     
40665     afterSlide : function(){
40666         this.grid.getView().scroller.unclip();
40667     },
40668     
40669     destroy : function(){
40670         this.grid.destroy();
40671         delete this.grid;
40672         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40673     }
40674 });
40675
40676 /**
40677  * @class Roo.bootstrap.panel.Nest
40678  * @extends Roo.bootstrap.panel.Content
40679  * @constructor
40680  * Create a new Panel, that can contain a layout.Border.
40681  * 
40682  * 
40683  * @param {Roo.BorderLayout} layout The layout for this panel
40684  * @param {String/Object} config A string to set only the title or a config object
40685  */
40686 Roo.bootstrap.panel.Nest = function(config)
40687 {
40688     // construct with only one argument..
40689     /* FIXME - implement nicer consturctors
40690     if (layout.layout) {
40691         config = layout;
40692         layout = config.layout;
40693         delete config.layout;
40694     }
40695     if (layout.xtype && !layout.getEl) {
40696         // then layout needs constructing..
40697         layout = Roo.factory(layout, Roo);
40698     }
40699     */
40700     
40701     config.el =  config.layout.getEl();
40702     
40703     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40704     
40705     config.layout.monitorWindowResize = false; // turn off autosizing
40706     this.layout = config.layout;
40707     this.layout.getEl().addClass("roo-layout-nested-layout");
40708     this.layout.parent = this;
40709     
40710     
40711     
40712     
40713 };
40714
40715 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40716
40717     setSize : function(width, height){
40718         if(!this.ignoreResize(width, height)){
40719             var size = this.adjustForComponents(width, height);
40720             var el = this.layout.getEl();
40721             if (size.height < 1) {
40722                 el.setWidth(size.width);   
40723             } else {
40724                 el.setSize(size.width, size.height);
40725             }
40726             var touch = el.dom.offsetWidth;
40727             this.layout.layout();
40728             // ie requires a double layout on the first pass
40729             if(Roo.isIE && !this.initialized){
40730                 this.initialized = true;
40731                 this.layout.layout();
40732             }
40733         }
40734     },
40735     
40736     // activate all subpanels if not currently active..
40737     
40738     setActiveState : function(active){
40739         this.active = active;
40740         this.setActiveClass(active);
40741         
40742         if(!active){
40743             this.fireEvent("deactivate", this);
40744             return;
40745         }
40746         
40747         this.fireEvent("activate", this);
40748         // not sure if this should happen before or after..
40749         if (!this.layout) {
40750             return; // should not happen..
40751         }
40752         var reg = false;
40753         for (var r in this.layout.regions) {
40754             reg = this.layout.getRegion(r);
40755             if (reg.getActivePanel()) {
40756                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40757                 reg.setActivePanel(reg.getActivePanel());
40758                 continue;
40759             }
40760             if (!reg.panels.length) {
40761                 continue;
40762             }
40763             reg.showPanel(reg.getPanel(0));
40764         }
40765         
40766         
40767         
40768         
40769     },
40770     
40771     /**
40772      * Returns the nested BorderLayout for this panel
40773      * @return {Roo.BorderLayout} 
40774      */
40775     getLayout : function(){
40776         return this.layout;
40777     },
40778     
40779      /**
40780      * Adds a xtype elements to the layout of the nested panel
40781      * <pre><code>
40782
40783 panel.addxtype({
40784        xtype : 'ContentPanel',
40785        region: 'west',
40786        items: [ .... ]
40787    }
40788 );
40789
40790 panel.addxtype({
40791         xtype : 'NestedLayoutPanel',
40792         region: 'west',
40793         layout: {
40794            center: { },
40795            west: { }   
40796         },
40797         items : [ ... list of content panels or nested layout panels.. ]
40798    }
40799 );
40800 </code></pre>
40801      * @param {Object} cfg Xtype definition of item to add.
40802      */
40803     addxtype : function(cfg) {
40804         return this.layout.addxtype(cfg);
40805     
40806     }
40807 });/*
40808  * Based on:
40809  * Ext JS Library 1.1.1
40810  * Copyright(c) 2006-2007, Ext JS, LLC.
40811  *
40812  * Originally Released Under LGPL - original licence link has changed is not relivant.
40813  *
40814  * Fork - LGPL
40815  * <script type="text/javascript">
40816  */
40817 /**
40818  * @class Roo.TabPanel
40819  * @extends Roo.util.Observable
40820  * A lightweight tab container.
40821  * <br><br>
40822  * Usage:
40823  * <pre><code>
40824 // basic tabs 1, built from existing content
40825 var tabs = new Roo.TabPanel("tabs1");
40826 tabs.addTab("script", "View Script");
40827 tabs.addTab("markup", "View Markup");
40828 tabs.activate("script");
40829
40830 // more advanced tabs, built from javascript
40831 var jtabs = new Roo.TabPanel("jtabs");
40832 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40833
40834 // set up the UpdateManager
40835 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40836 var updater = tab2.getUpdateManager();
40837 updater.setDefaultUrl("ajax1.htm");
40838 tab2.on('activate', updater.refresh, updater, true);
40839
40840 // Use setUrl for Ajax loading
40841 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40842 tab3.setUrl("ajax2.htm", null, true);
40843
40844 // Disabled tab
40845 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40846 tab4.disable();
40847
40848 jtabs.activate("jtabs-1");
40849  * </code></pre>
40850  * @constructor
40851  * Create a new TabPanel.
40852  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40853  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40854  */
40855 Roo.bootstrap.panel.Tabs = function(config){
40856     /**
40857     * The container element for this TabPanel.
40858     * @type Roo.Element
40859     */
40860     this.el = Roo.get(config.el);
40861     delete config.el;
40862     if(config){
40863         if(typeof config == "boolean"){
40864             this.tabPosition = config ? "bottom" : "top";
40865         }else{
40866             Roo.apply(this, config);
40867         }
40868     }
40869     
40870     if(this.tabPosition == "bottom"){
40871         // if tabs are at the bottom = create the body first.
40872         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40873         this.el.addClass("roo-tabs-bottom");
40874     }
40875     // next create the tabs holders
40876     
40877     if (this.tabPosition == "west"){
40878         
40879         var reg = this.region; // fake it..
40880         while (reg) {
40881             if (!reg.mgr.parent) {
40882                 break;
40883             }
40884             reg = reg.mgr.parent.region;
40885         }
40886         Roo.log("got nest?");
40887         Roo.log(reg);
40888         if (reg.mgr.getRegion('west')) {
40889             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40890             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40891             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40892             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40893             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40894         
40895             
40896         }
40897         
40898         
40899     } else {
40900      
40901         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40902         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40903         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40904         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40905     }
40906     
40907     
40908     if(Roo.isIE){
40909         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40910     }
40911     
40912     // finally - if tabs are at the top, then create the body last..
40913     if(this.tabPosition != "bottom"){
40914         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40915          * @type Roo.Element
40916          */
40917         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40918         this.el.addClass("roo-tabs-top");
40919     }
40920     this.items = [];
40921
40922     this.bodyEl.setStyle("position", "relative");
40923
40924     this.active = null;
40925     this.activateDelegate = this.activate.createDelegate(this);
40926
40927     this.addEvents({
40928         /**
40929          * @event tabchange
40930          * Fires when the active tab changes
40931          * @param {Roo.TabPanel} this
40932          * @param {Roo.TabPanelItem} activePanel The new active tab
40933          */
40934         "tabchange": true,
40935         /**
40936          * @event beforetabchange
40937          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40938          * @param {Roo.TabPanel} this
40939          * @param {Object} e Set cancel to true on this object to cancel the tab change
40940          * @param {Roo.TabPanelItem} tab The tab being changed to
40941          */
40942         "beforetabchange" : true
40943     });
40944
40945     Roo.EventManager.onWindowResize(this.onResize, this);
40946     this.cpad = this.el.getPadding("lr");
40947     this.hiddenCount = 0;
40948
40949
40950     // toolbar on the tabbar support...
40951     if (this.toolbar) {
40952         alert("no toolbar support yet");
40953         this.toolbar  = false;
40954         /*
40955         var tcfg = this.toolbar;
40956         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40957         this.toolbar = new Roo.Toolbar(tcfg);
40958         if (Roo.isSafari) {
40959             var tbl = tcfg.container.child('table', true);
40960             tbl.setAttribute('width', '100%');
40961         }
40962         */
40963         
40964     }
40965    
40966
40967
40968     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40969 };
40970
40971 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40972     /*
40973      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40974      */
40975     tabPosition : "top",
40976     /*
40977      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40978      */
40979     currentTabWidth : 0,
40980     /*
40981      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40982      */
40983     minTabWidth : 40,
40984     /*
40985      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40986      */
40987     maxTabWidth : 250,
40988     /*
40989      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40990      */
40991     preferredTabWidth : 175,
40992     /*
40993      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40994      */
40995     resizeTabs : false,
40996     /*
40997      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40998      */
40999     monitorResize : true,
41000     /*
41001      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41002      */
41003     toolbar : false,  // set by caller..
41004     
41005     region : false, /// set by caller
41006     
41007     disableTooltips : true, // not used yet...
41008
41009     /**
41010      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41011      * @param {String} id The id of the div to use <b>or create</b>
41012      * @param {String} text The text for the tab
41013      * @param {String} content (optional) Content to put in the TabPanelItem body
41014      * @param {Boolean} closable (optional) True to create a close icon on the tab
41015      * @return {Roo.TabPanelItem} The created TabPanelItem
41016      */
41017     addTab : function(id, text, content, closable, tpl)
41018     {
41019         var item = new Roo.bootstrap.panel.TabItem({
41020             panel: this,
41021             id : id,
41022             text : text,
41023             closable : closable,
41024             tpl : tpl
41025         });
41026         this.addTabItem(item);
41027         if(content){
41028             item.setContent(content);
41029         }
41030         return item;
41031     },
41032
41033     /**
41034      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41035      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41036      * @return {Roo.TabPanelItem}
41037      */
41038     getTab : function(id){
41039         return this.items[id];
41040     },
41041
41042     /**
41043      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41044      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41045      */
41046     hideTab : function(id){
41047         var t = this.items[id];
41048         if(!t.isHidden()){
41049            t.setHidden(true);
41050            this.hiddenCount++;
41051            this.autoSizeTabs();
41052         }
41053     },
41054
41055     /**
41056      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41057      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41058      */
41059     unhideTab : function(id){
41060         var t = this.items[id];
41061         if(t.isHidden()){
41062            t.setHidden(false);
41063            this.hiddenCount--;
41064            this.autoSizeTabs();
41065         }
41066     },
41067
41068     /**
41069      * Adds an existing {@link Roo.TabPanelItem}.
41070      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41071      */
41072     addTabItem : function(item)
41073     {
41074         this.items[item.id] = item;
41075         this.items.push(item);
41076         this.autoSizeTabs();
41077       //  if(this.resizeTabs){
41078     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41079   //         this.autoSizeTabs();
41080 //        }else{
41081 //            item.autoSize();
41082        // }
41083     },
41084
41085     /**
41086      * Removes a {@link Roo.TabPanelItem}.
41087      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41088      */
41089     removeTab : function(id){
41090         var items = this.items;
41091         var tab = items[id];
41092         if(!tab) { return; }
41093         var index = items.indexOf(tab);
41094         if(this.active == tab && items.length > 1){
41095             var newTab = this.getNextAvailable(index);
41096             if(newTab) {
41097                 newTab.activate();
41098             }
41099         }
41100         this.stripEl.dom.removeChild(tab.pnode.dom);
41101         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41102             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41103         }
41104         items.splice(index, 1);
41105         delete this.items[tab.id];
41106         tab.fireEvent("close", tab);
41107         tab.purgeListeners();
41108         this.autoSizeTabs();
41109     },
41110
41111     getNextAvailable : function(start){
41112         var items = this.items;
41113         var index = start;
41114         // look for a next tab that will slide over to
41115         // replace the one being removed
41116         while(index < items.length){
41117             var item = items[++index];
41118             if(item && !item.isHidden()){
41119                 return item;
41120             }
41121         }
41122         // if one isn't found select the previous tab (on the left)
41123         index = start;
41124         while(index >= 0){
41125             var item = items[--index];
41126             if(item && !item.isHidden()){
41127                 return item;
41128             }
41129         }
41130         return null;
41131     },
41132
41133     /**
41134      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41135      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41136      */
41137     disableTab : function(id){
41138         var tab = this.items[id];
41139         if(tab && this.active != tab){
41140             tab.disable();
41141         }
41142     },
41143
41144     /**
41145      * Enables a {@link Roo.TabPanelItem} that is disabled.
41146      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41147      */
41148     enableTab : function(id){
41149         var tab = this.items[id];
41150         tab.enable();
41151     },
41152
41153     /**
41154      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41155      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41156      * @return {Roo.TabPanelItem} The TabPanelItem.
41157      */
41158     activate : function(id)
41159     {
41160         //Roo.log('activite:'  + id);
41161         
41162         var tab = this.items[id];
41163         if(!tab){
41164             return null;
41165         }
41166         if(tab == this.active || tab.disabled){
41167             return tab;
41168         }
41169         var e = {};
41170         this.fireEvent("beforetabchange", this, e, tab);
41171         if(e.cancel !== true && !tab.disabled){
41172             if(this.active){
41173                 this.active.hide();
41174             }
41175             this.active = this.items[id];
41176             this.active.show();
41177             this.fireEvent("tabchange", this, this.active);
41178         }
41179         return tab;
41180     },
41181
41182     /**
41183      * Gets the active {@link Roo.TabPanelItem}.
41184      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41185      */
41186     getActiveTab : function(){
41187         return this.active;
41188     },
41189
41190     /**
41191      * Updates the tab body element to fit the height of the container element
41192      * for overflow scrolling
41193      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41194      */
41195     syncHeight : function(targetHeight){
41196         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41197         var bm = this.bodyEl.getMargins();
41198         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41199         this.bodyEl.setHeight(newHeight);
41200         return newHeight;
41201     },
41202
41203     onResize : function(){
41204         if(this.monitorResize){
41205             this.autoSizeTabs();
41206         }
41207     },
41208
41209     /**
41210      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41211      */
41212     beginUpdate : function(){
41213         this.updating = true;
41214     },
41215
41216     /**
41217      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41218      */
41219     endUpdate : function(){
41220         this.updating = false;
41221         this.autoSizeTabs();
41222     },
41223
41224     /**
41225      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41226      */
41227     autoSizeTabs : function()
41228     {
41229         var count = this.items.length;
41230         var vcount = count - this.hiddenCount;
41231         
41232         if (vcount < 2) {
41233             this.stripEl.hide();
41234         } else {
41235             this.stripEl.show();
41236         }
41237         
41238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41239             return;
41240         }
41241         
41242         
41243         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41244         var availWidth = Math.floor(w / vcount);
41245         var b = this.stripBody;
41246         if(b.getWidth() > w){
41247             var tabs = this.items;
41248             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41249             if(availWidth < this.minTabWidth){
41250                 /*if(!this.sleft){    // incomplete scrolling code
41251                     this.createScrollButtons();
41252                 }
41253                 this.showScroll();
41254                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41255             }
41256         }else{
41257             if(this.currentTabWidth < this.preferredTabWidth){
41258                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41259             }
41260         }
41261     },
41262
41263     /**
41264      * Returns the number of tabs in this TabPanel.
41265      * @return {Number}
41266      */
41267      getCount : function(){
41268          return this.items.length;
41269      },
41270
41271     /**
41272      * Resizes all the tabs to the passed width
41273      * @param {Number} The new width
41274      */
41275     setTabWidth : function(width){
41276         this.currentTabWidth = width;
41277         for(var i = 0, len = this.items.length; i < len; i++) {
41278                 if(!this.items[i].isHidden()) {
41279                 this.items[i].setWidth(width);
41280             }
41281         }
41282     },
41283
41284     /**
41285      * Destroys this TabPanel
41286      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41287      */
41288     destroy : function(removeEl){
41289         Roo.EventManager.removeResizeListener(this.onResize, this);
41290         for(var i = 0, len = this.items.length; i < len; i++){
41291             this.items[i].purgeListeners();
41292         }
41293         if(removeEl === true){
41294             this.el.update("");
41295             this.el.remove();
41296         }
41297     },
41298     
41299     createStrip : function(container)
41300     {
41301         var strip = document.createElement("nav");
41302         strip.className = Roo.bootstrap.version == 4 ?
41303             "navbar-light bg-light" : 
41304             "navbar navbar-default"; //"x-tabs-wrap";
41305         container.appendChild(strip);
41306         return strip;
41307     },
41308     
41309     createStripList : function(strip)
41310     {
41311         // div wrapper for retard IE
41312         // returns the "tr" element.
41313         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41314         //'<div class="x-tabs-strip-wrap">'+
41315           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41316           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41317         return strip.firstChild; //.firstChild.firstChild.firstChild;
41318     },
41319     createBody : function(container)
41320     {
41321         var body = document.createElement("div");
41322         Roo.id(body, "tab-body");
41323         //Roo.fly(body).addClass("x-tabs-body");
41324         Roo.fly(body).addClass("tab-content");
41325         container.appendChild(body);
41326         return body;
41327     },
41328     createItemBody :function(bodyEl, id){
41329         var body = Roo.getDom(id);
41330         if(!body){
41331             body = document.createElement("div");
41332             body.id = id;
41333         }
41334         //Roo.fly(body).addClass("x-tabs-item-body");
41335         Roo.fly(body).addClass("tab-pane");
41336          bodyEl.insertBefore(body, bodyEl.firstChild);
41337         return body;
41338     },
41339     /** @private */
41340     createStripElements :  function(stripEl, text, closable, tpl)
41341     {
41342         var td = document.createElement("li"); // was td..
41343         td.className = 'nav-item';
41344         
41345         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41346         
41347         
41348         stripEl.appendChild(td);
41349         /*if(closable){
41350             td.className = "x-tabs-closable";
41351             if(!this.closeTpl){
41352                 this.closeTpl = new Roo.Template(
41353                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41354                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41355                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41356                 );
41357             }
41358             var el = this.closeTpl.overwrite(td, {"text": text});
41359             var close = el.getElementsByTagName("div")[0];
41360             var inner = el.getElementsByTagName("em")[0];
41361             return {"el": el, "close": close, "inner": inner};
41362         } else {
41363         */
41364         // not sure what this is..
41365 //            if(!this.tabTpl){
41366                 //this.tabTpl = new Roo.Template(
41367                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41368                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41369                 //);
41370 //                this.tabTpl = new Roo.Template(
41371 //                   '<a href="#">' +
41372 //                   '<span unselectable="on"' +
41373 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41374 //                            ' >{text}</span></a>'
41375 //                );
41376 //                
41377 //            }
41378
41379
41380             var template = tpl || this.tabTpl || false;
41381             
41382             if(!template){
41383                 template =  new Roo.Template(
41384                         Roo.bootstrap.version == 4 ? 
41385                             (
41386                                 '<a class="nav-link" href="#" unselectable="on"' +
41387                                      (this.disableTooltips ? '' : ' title="{text}"') +
41388                                      ' >{text}</a>'
41389                             ) : (
41390                                 '<a class="nav-link" href="#">' +
41391                                 '<span unselectable="on"' +
41392                                          (this.disableTooltips ? '' : ' title="{text}"') +
41393                                     ' >{text}</span></a>'
41394                             )
41395                 );
41396             }
41397             
41398             switch (typeof(template)) {
41399                 case 'object' :
41400                     break;
41401                 case 'string' :
41402                     template = new Roo.Template(template);
41403                     break;
41404                 default :
41405                     break;
41406             }
41407             
41408             var el = template.overwrite(td, {"text": text});
41409             
41410             var inner = el.getElementsByTagName("span")[0];
41411             
41412             return {"el": el, "inner": inner};
41413             
41414     }
41415         
41416     
41417 });
41418
41419 /**
41420  * @class Roo.TabPanelItem
41421  * @extends Roo.util.Observable
41422  * Represents an individual item (tab plus body) in a TabPanel.
41423  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41424  * @param {String} id The id of this TabPanelItem
41425  * @param {String} text The text for the tab of this TabPanelItem
41426  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41427  */
41428 Roo.bootstrap.panel.TabItem = function(config){
41429     /**
41430      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41431      * @type Roo.TabPanel
41432      */
41433     this.tabPanel = config.panel;
41434     /**
41435      * The id for this TabPanelItem
41436      * @type String
41437      */
41438     this.id = config.id;
41439     /** @private */
41440     this.disabled = false;
41441     /** @private */
41442     this.text = config.text;
41443     /** @private */
41444     this.loaded = false;
41445     this.closable = config.closable;
41446
41447     /**
41448      * The body element for this TabPanelItem.
41449      * @type Roo.Element
41450      */
41451     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41452     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41453     this.bodyEl.setStyle("display", "block");
41454     this.bodyEl.setStyle("zoom", "1");
41455     //this.hideAction();
41456
41457     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41458     /** @private */
41459     this.el = Roo.get(els.el);
41460     this.inner = Roo.get(els.inner, true);
41461      this.textEl = Roo.bootstrap.version == 4 ?
41462         this.el : Roo.get(this.el.dom.firstChild, true);
41463
41464     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41465     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41466
41467     
41468 //    this.el.on("mousedown", this.onTabMouseDown, this);
41469     this.el.on("click", this.onTabClick, this);
41470     /** @private */
41471     if(config.closable){
41472         var c = Roo.get(els.close, true);
41473         c.dom.title = this.closeText;
41474         c.addClassOnOver("close-over");
41475         c.on("click", this.closeClick, this);
41476      }
41477
41478     this.addEvents({
41479          /**
41480          * @event activate
41481          * Fires when this tab becomes the active tab.
41482          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41483          * @param {Roo.TabPanelItem} this
41484          */
41485         "activate": true,
41486         /**
41487          * @event beforeclose
41488          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41489          * @param {Roo.TabPanelItem} this
41490          * @param {Object} e Set cancel to true on this object to cancel the close.
41491          */
41492         "beforeclose": true,
41493         /**
41494          * @event close
41495          * Fires when this tab is closed.
41496          * @param {Roo.TabPanelItem} this
41497          */
41498          "close": true,
41499         /**
41500          * @event deactivate
41501          * Fires when this tab is no longer the active tab.
41502          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41503          * @param {Roo.TabPanelItem} this
41504          */
41505          "deactivate" : true
41506     });
41507     this.hidden = false;
41508
41509     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41510 };
41511
41512 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41513            {
41514     purgeListeners : function(){
41515        Roo.util.Observable.prototype.purgeListeners.call(this);
41516        this.el.removeAllListeners();
41517     },
41518     /**
41519      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41520      */
41521     show : function(){
41522         this.status_node.addClass("active");
41523         this.showAction();
41524         if(Roo.isOpera){
41525             this.tabPanel.stripWrap.repaint();
41526         }
41527         this.fireEvent("activate", this.tabPanel, this);
41528     },
41529
41530     /**
41531      * Returns true if this tab is the active tab.
41532      * @return {Boolean}
41533      */
41534     isActive : function(){
41535         return this.tabPanel.getActiveTab() == this;
41536     },
41537
41538     /**
41539      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41540      */
41541     hide : function(){
41542         this.status_node.removeClass("active");
41543         this.hideAction();
41544         this.fireEvent("deactivate", this.tabPanel, this);
41545     },
41546
41547     hideAction : function(){
41548         this.bodyEl.hide();
41549         this.bodyEl.setStyle("position", "absolute");
41550         this.bodyEl.setLeft("-20000px");
41551         this.bodyEl.setTop("-20000px");
41552     },
41553
41554     showAction : function(){
41555         this.bodyEl.setStyle("position", "relative");
41556         this.bodyEl.setTop("");
41557         this.bodyEl.setLeft("");
41558         this.bodyEl.show();
41559     },
41560
41561     /**
41562      * Set the tooltip for the tab.
41563      * @param {String} tooltip The tab's tooltip
41564      */
41565     setTooltip : function(text){
41566         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41567             this.textEl.dom.qtip = text;
41568             this.textEl.dom.removeAttribute('title');
41569         }else{
41570             this.textEl.dom.title = text;
41571         }
41572     },
41573
41574     onTabClick : function(e){
41575         e.preventDefault();
41576         this.tabPanel.activate(this.id);
41577     },
41578
41579     onTabMouseDown : function(e){
41580         e.preventDefault();
41581         this.tabPanel.activate(this.id);
41582     },
41583 /*
41584     getWidth : function(){
41585         return this.inner.getWidth();
41586     },
41587
41588     setWidth : function(width){
41589         var iwidth = width - this.linode.getPadding("lr");
41590         this.inner.setWidth(iwidth);
41591         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41592         this.linode.setWidth(width);
41593     },
41594 */
41595     /**
41596      * Show or hide the tab
41597      * @param {Boolean} hidden True to hide or false to show.
41598      */
41599     setHidden : function(hidden){
41600         this.hidden = hidden;
41601         this.linode.setStyle("display", hidden ? "none" : "");
41602     },
41603
41604     /**
41605      * Returns true if this tab is "hidden"
41606      * @return {Boolean}
41607      */
41608     isHidden : function(){
41609         return this.hidden;
41610     },
41611
41612     /**
41613      * Returns the text for this tab
41614      * @return {String}
41615      */
41616     getText : function(){
41617         return this.text;
41618     },
41619     /*
41620     autoSize : function(){
41621         //this.el.beginMeasure();
41622         this.textEl.setWidth(1);
41623         /*
41624          *  #2804 [new] Tabs in Roojs
41625          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41626          */
41627         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41628         //this.el.endMeasure();
41629     //},
41630
41631     /**
41632      * Sets the text for the tab (Note: this also sets the tooltip text)
41633      * @param {String} text The tab's text and tooltip
41634      */
41635     setText : function(text){
41636         this.text = text;
41637         this.textEl.update(text);
41638         this.setTooltip(text);
41639         //if(!this.tabPanel.resizeTabs){
41640         //    this.autoSize();
41641         //}
41642     },
41643     /**
41644      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41645      */
41646     activate : function(){
41647         this.tabPanel.activate(this.id);
41648     },
41649
41650     /**
41651      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41652      */
41653     disable : function(){
41654         if(this.tabPanel.active != this){
41655             this.disabled = true;
41656             this.status_node.addClass("disabled");
41657         }
41658     },
41659
41660     /**
41661      * Enables this TabPanelItem if it was previously disabled.
41662      */
41663     enable : function(){
41664         this.disabled = false;
41665         this.status_node.removeClass("disabled");
41666     },
41667
41668     /**
41669      * Sets the content for this TabPanelItem.
41670      * @param {String} content The content
41671      * @param {Boolean} loadScripts true to look for and load scripts
41672      */
41673     setContent : function(content, loadScripts){
41674         this.bodyEl.update(content, loadScripts);
41675     },
41676
41677     /**
41678      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41679      * @return {Roo.UpdateManager} The UpdateManager
41680      */
41681     getUpdateManager : function(){
41682         return this.bodyEl.getUpdateManager();
41683     },
41684
41685     /**
41686      * Set a URL to be used to load the content for this TabPanelItem.
41687      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41688      * @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)
41689      * @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)
41690      * @return {Roo.UpdateManager} The UpdateManager
41691      */
41692     setUrl : function(url, params, loadOnce){
41693         if(this.refreshDelegate){
41694             this.un('activate', this.refreshDelegate);
41695         }
41696         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41697         this.on("activate", this.refreshDelegate);
41698         return this.bodyEl.getUpdateManager();
41699     },
41700
41701     /** @private */
41702     _handleRefresh : function(url, params, loadOnce){
41703         if(!loadOnce || !this.loaded){
41704             var updater = this.bodyEl.getUpdateManager();
41705             updater.update(url, params, this._setLoaded.createDelegate(this));
41706         }
41707     },
41708
41709     /**
41710      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41711      *   Will fail silently if the setUrl method has not been called.
41712      *   This does not activate the panel, just updates its content.
41713      */
41714     refresh : function(){
41715         if(this.refreshDelegate){
41716            this.loaded = false;
41717            this.refreshDelegate();
41718         }
41719     },
41720
41721     /** @private */
41722     _setLoaded : function(){
41723         this.loaded = true;
41724     },
41725
41726     /** @private */
41727     closeClick : function(e){
41728         var o = {};
41729         e.stopEvent();
41730         this.fireEvent("beforeclose", this, o);
41731         if(o.cancel !== true){
41732             this.tabPanel.removeTab(this.id);
41733         }
41734     },
41735     /**
41736      * The text displayed in the tooltip for the close icon.
41737      * @type String
41738      */
41739     closeText : "Close this tab"
41740 });
41741 /**
41742 *    This script refer to:
41743 *    Title: International Telephone Input
41744 *    Author: Jack O'Connor
41745 *    Code version:  v12.1.12
41746 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41747 **/
41748
41749 Roo.bootstrap.PhoneInputData = function() {
41750     var d = [
41751       [
41752         "Afghanistan (‫افغانستان‬‎)",
41753         "af",
41754         "93"
41755       ],
41756       [
41757         "Albania (Shqipëri)",
41758         "al",
41759         "355"
41760       ],
41761       [
41762         "Algeria (‫الجزائر‬‎)",
41763         "dz",
41764         "213"
41765       ],
41766       [
41767         "American Samoa",
41768         "as",
41769         "1684"
41770       ],
41771       [
41772         "Andorra",
41773         "ad",
41774         "376"
41775       ],
41776       [
41777         "Angola",
41778         "ao",
41779         "244"
41780       ],
41781       [
41782         "Anguilla",
41783         "ai",
41784         "1264"
41785       ],
41786       [
41787         "Antigua and Barbuda",
41788         "ag",
41789         "1268"
41790       ],
41791       [
41792         "Argentina",
41793         "ar",
41794         "54"
41795       ],
41796       [
41797         "Armenia (Հայաստան)",
41798         "am",
41799         "374"
41800       ],
41801       [
41802         "Aruba",
41803         "aw",
41804         "297"
41805       ],
41806       [
41807         "Australia",
41808         "au",
41809         "61",
41810         0
41811       ],
41812       [
41813         "Austria (Österreich)",
41814         "at",
41815         "43"
41816       ],
41817       [
41818         "Azerbaijan (Azərbaycan)",
41819         "az",
41820         "994"
41821       ],
41822       [
41823         "Bahamas",
41824         "bs",
41825         "1242"
41826       ],
41827       [
41828         "Bahrain (‫البحرين‬‎)",
41829         "bh",
41830         "973"
41831       ],
41832       [
41833         "Bangladesh (বাংলাদেশ)",
41834         "bd",
41835         "880"
41836       ],
41837       [
41838         "Barbados",
41839         "bb",
41840         "1246"
41841       ],
41842       [
41843         "Belarus (Беларусь)",
41844         "by",
41845         "375"
41846       ],
41847       [
41848         "Belgium (België)",
41849         "be",
41850         "32"
41851       ],
41852       [
41853         "Belize",
41854         "bz",
41855         "501"
41856       ],
41857       [
41858         "Benin (Bénin)",
41859         "bj",
41860         "229"
41861       ],
41862       [
41863         "Bermuda",
41864         "bm",
41865         "1441"
41866       ],
41867       [
41868         "Bhutan (འབྲུག)",
41869         "bt",
41870         "975"
41871       ],
41872       [
41873         "Bolivia",
41874         "bo",
41875         "591"
41876       ],
41877       [
41878         "Bosnia and Herzegovina (Босна и Херцеговина)",
41879         "ba",
41880         "387"
41881       ],
41882       [
41883         "Botswana",
41884         "bw",
41885         "267"
41886       ],
41887       [
41888         "Brazil (Brasil)",
41889         "br",
41890         "55"
41891       ],
41892       [
41893         "British Indian Ocean Territory",
41894         "io",
41895         "246"
41896       ],
41897       [
41898         "British Virgin Islands",
41899         "vg",
41900         "1284"
41901       ],
41902       [
41903         "Brunei",
41904         "bn",
41905         "673"
41906       ],
41907       [
41908         "Bulgaria (България)",
41909         "bg",
41910         "359"
41911       ],
41912       [
41913         "Burkina Faso",
41914         "bf",
41915         "226"
41916       ],
41917       [
41918         "Burundi (Uburundi)",
41919         "bi",
41920         "257"
41921       ],
41922       [
41923         "Cambodia (កម្ពុជា)",
41924         "kh",
41925         "855"
41926       ],
41927       [
41928         "Cameroon (Cameroun)",
41929         "cm",
41930         "237"
41931       ],
41932       [
41933         "Canada",
41934         "ca",
41935         "1",
41936         1,
41937         ["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"]
41938       ],
41939       [
41940         "Cape Verde (Kabu Verdi)",
41941         "cv",
41942         "238"
41943       ],
41944       [
41945         "Caribbean Netherlands",
41946         "bq",
41947         "599",
41948         1
41949       ],
41950       [
41951         "Cayman Islands",
41952         "ky",
41953         "1345"
41954       ],
41955       [
41956         "Central African Republic (République centrafricaine)",
41957         "cf",
41958         "236"
41959       ],
41960       [
41961         "Chad (Tchad)",
41962         "td",
41963         "235"
41964       ],
41965       [
41966         "Chile",
41967         "cl",
41968         "56"
41969       ],
41970       [
41971         "China (中国)",
41972         "cn",
41973         "86"
41974       ],
41975       [
41976         "Christmas Island",
41977         "cx",
41978         "61",
41979         2
41980       ],
41981       [
41982         "Cocos (Keeling) Islands",
41983         "cc",
41984         "61",
41985         1
41986       ],
41987       [
41988         "Colombia",
41989         "co",
41990         "57"
41991       ],
41992       [
41993         "Comoros (‫جزر القمر‬‎)",
41994         "km",
41995         "269"
41996       ],
41997       [
41998         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41999         "cd",
42000         "243"
42001       ],
42002       [
42003         "Congo (Republic) (Congo-Brazzaville)",
42004         "cg",
42005         "242"
42006       ],
42007       [
42008         "Cook Islands",
42009         "ck",
42010         "682"
42011       ],
42012       [
42013         "Costa Rica",
42014         "cr",
42015         "506"
42016       ],
42017       [
42018         "Côte d’Ivoire",
42019         "ci",
42020         "225"
42021       ],
42022       [
42023         "Croatia (Hrvatska)",
42024         "hr",
42025         "385"
42026       ],
42027       [
42028         "Cuba",
42029         "cu",
42030         "53"
42031       ],
42032       [
42033         "Curaçao",
42034         "cw",
42035         "599",
42036         0
42037       ],
42038       [
42039         "Cyprus (Κύπρος)",
42040         "cy",
42041         "357"
42042       ],
42043       [
42044         "Czech Republic (Česká republika)",
42045         "cz",
42046         "420"
42047       ],
42048       [
42049         "Denmark (Danmark)",
42050         "dk",
42051         "45"
42052       ],
42053       [
42054         "Djibouti",
42055         "dj",
42056         "253"
42057       ],
42058       [
42059         "Dominica",
42060         "dm",
42061         "1767"
42062       ],
42063       [
42064         "Dominican Republic (República Dominicana)",
42065         "do",
42066         "1",
42067         2,
42068         ["809", "829", "849"]
42069       ],
42070       [
42071         "Ecuador",
42072         "ec",
42073         "593"
42074       ],
42075       [
42076         "Egypt (‫مصر‬‎)",
42077         "eg",
42078         "20"
42079       ],
42080       [
42081         "El Salvador",
42082         "sv",
42083         "503"
42084       ],
42085       [
42086         "Equatorial Guinea (Guinea Ecuatorial)",
42087         "gq",
42088         "240"
42089       ],
42090       [
42091         "Eritrea",
42092         "er",
42093         "291"
42094       ],
42095       [
42096         "Estonia (Eesti)",
42097         "ee",
42098         "372"
42099       ],
42100       [
42101         "Ethiopia",
42102         "et",
42103         "251"
42104       ],
42105       [
42106         "Falkland Islands (Islas Malvinas)",
42107         "fk",
42108         "500"
42109       ],
42110       [
42111         "Faroe Islands (Føroyar)",
42112         "fo",
42113         "298"
42114       ],
42115       [
42116         "Fiji",
42117         "fj",
42118         "679"
42119       ],
42120       [
42121         "Finland (Suomi)",
42122         "fi",
42123         "358",
42124         0
42125       ],
42126       [
42127         "France",
42128         "fr",
42129         "33"
42130       ],
42131       [
42132         "French Guiana (Guyane française)",
42133         "gf",
42134         "594"
42135       ],
42136       [
42137         "French Polynesia (Polynésie française)",
42138         "pf",
42139         "689"
42140       ],
42141       [
42142         "Gabon",
42143         "ga",
42144         "241"
42145       ],
42146       [
42147         "Gambia",
42148         "gm",
42149         "220"
42150       ],
42151       [
42152         "Georgia (საქართველო)",
42153         "ge",
42154         "995"
42155       ],
42156       [
42157         "Germany (Deutschland)",
42158         "de",
42159         "49"
42160       ],
42161       [
42162         "Ghana (Gaana)",
42163         "gh",
42164         "233"
42165       ],
42166       [
42167         "Gibraltar",
42168         "gi",
42169         "350"
42170       ],
42171       [
42172         "Greece (Ελλάδα)",
42173         "gr",
42174         "30"
42175       ],
42176       [
42177         "Greenland (Kalaallit Nunaat)",
42178         "gl",
42179         "299"
42180       ],
42181       [
42182         "Grenada",
42183         "gd",
42184         "1473"
42185       ],
42186       [
42187         "Guadeloupe",
42188         "gp",
42189         "590",
42190         0
42191       ],
42192       [
42193         "Guam",
42194         "gu",
42195         "1671"
42196       ],
42197       [
42198         "Guatemala",
42199         "gt",
42200         "502"
42201       ],
42202       [
42203         "Guernsey",
42204         "gg",
42205         "44",
42206         1
42207       ],
42208       [
42209         "Guinea (Guinée)",
42210         "gn",
42211         "224"
42212       ],
42213       [
42214         "Guinea-Bissau (Guiné Bissau)",
42215         "gw",
42216         "245"
42217       ],
42218       [
42219         "Guyana",
42220         "gy",
42221         "592"
42222       ],
42223       [
42224         "Haiti",
42225         "ht",
42226         "509"
42227       ],
42228       [
42229         "Honduras",
42230         "hn",
42231         "504"
42232       ],
42233       [
42234         "Hong Kong (香港)",
42235         "hk",
42236         "852"
42237       ],
42238       [
42239         "Hungary (Magyarország)",
42240         "hu",
42241         "36"
42242       ],
42243       [
42244         "Iceland (Ísland)",
42245         "is",
42246         "354"
42247       ],
42248       [
42249         "India (भारत)",
42250         "in",
42251         "91"
42252       ],
42253       [
42254         "Indonesia",
42255         "id",
42256         "62"
42257       ],
42258       [
42259         "Iran (‫ایران‬‎)",
42260         "ir",
42261         "98"
42262       ],
42263       [
42264         "Iraq (‫العراق‬‎)",
42265         "iq",
42266         "964"
42267       ],
42268       [
42269         "Ireland",
42270         "ie",
42271         "353"
42272       ],
42273       [
42274         "Isle of Man",
42275         "im",
42276         "44",
42277         2
42278       ],
42279       [
42280         "Israel (‫ישראל‬‎)",
42281         "il",
42282         "972"
42283       ],
42284       [
42285         "Italy (Italia)",
42286         "it",
42287         "39",
42288         0
42289       ],
42290       [
42291         "Jamaica",
42292         "jm",
42293         "1876"
42294       ],
42295       [
42296         "Japan (日本)",
42297         "jp",
42298         "81"
42299       ],
42300       [
42301         "Jersey",
42302         "je",
42303         "44",
42304         3
42305       ],
42306       [
42307         "Jordan (‫الأردن‬‎)",
42308         "jo",
42309         "962"
42310       ],
42311       [
42312         "Kazakhstan (Казахстан)",
42313         "kz",
42314         "7",
42315         1
42316       ],
42317       [
42318         "Kenya",
42319         "ke",
42320         "254"
42321       ],
42322       [
42323         "Kiribati",
42324         "ki",
42325         "686"
42326       ],
42327       [
42328         "Kosovo",
42329         "xk",
42330         "383"
42331       ],
42332       [
42333         "Kuwait (‫الكويت‬‎)",
42334         "kw",
42335         "965"
42336       ],
42337       [
42338         "Kyrgyzstan (Кыргызстан)",
42339         "kg",
42340         "996"
42341       ],
42342       [
42343         "Laos (ລາວ)",
42344         "la",
42345         "856"
42346       ],
42347       [
42348         "Latvia (Latvija)",
42349         "lv",
42350         "371"
42351       ],
42352       [
42353         "Lebanon (‫لبنان‬‎)",
42354         "lb",
42355         "961"
42356       ],
42357       [
42358         "Lesotho",
42359         "ls",
42360         "266"
42361       ],
42362       [
42363         "Liberia",
42364         "lr",
42365         "231"
42366       ],
42367       [
42368         "Libya (‫ليبيا‬‎)",
42369         "ly",
42370         "218"
42371       ],
42372       [
42373         "Liechtenstein",
42374         "li",
42375         "423"
42376       ],
42377       [
42378         "Lithuania (Lietuva)",
42379         "lt",
42380         "370"
42381       ],
42382       [
42383         "Luxembourg",
42384         "lu",
42385         "352"
42386       ],
42387       [
42388         "Macau (澳門)",
42389         "mo",
42390         "853"
42391       ],
42392       [
42393         "Macedonia (FYROM) (Македонија)",
42394         "mk",
42395         "389"
42396       ],
42397       [
42398         "Madagascar (Madagasikara)",
42399         "mg",
42400         "261"
42401       ],
42402       [
42403         "Malawi",
42404         "mw",
42405         "265"
42406       ],
42407       [
42408         "Malaysia",
42409         "my",
42410         "60"
42411       ],
42412       [
42413         "Maldives",
42414         "mv",
42415         "960"
42416       ],
42417       [
42418         "Mali",
42419         "ml",
42420         "223"
42421       ],
42422       [
42423         "Malta",
42424         "mt",
42425         "356"
42426       ],
42427       [
42428         "Marshall Islands",
42429         "mh",
42430         "692"
42431       ],
42432       [
42433         "Martinique",
42434         "mq",
42435         "596"
42436       ],
42437       [
42438         "Mauritania (‫موريتانيا‬‎)",
42439         "mr",
42440         "222"
42441       ],
42442       [
42443         "Mauritius (Moris)",
42444         "mu",
42445         "230"
42446       ],
42447       [
42448         "Mayotte",
42449         "yt",
42450         "262",
42451         1
42452       ],
42453       [
42454         "Mexico (México)",
42455         "mx",
42456         "52"
42457       ],
42458       [
42459         "Micronesia",
42460         "fm",
42461         "691"
42462       ],
42463       [
42464         "Moldova (Republica Moldova)",
42465         "md",
42466         "373"
42467       ],
42468       [
42469         "Monaco",
42470         "mc",
42471         "377"
42472       ],
42473       [
42474         "Mongolia (Монгол)",
42475         "mn",
42476         "976"
42477       ],
42478       [
42479         "Montenegro (Crna Gora)",
42480         "me",
42481         "382"
42482       ],
42483       [
42484         "Montserrat",
42485         "ms",
42486         "1664"
42487       ],
42488       [
42489         "Morocco (‫المغرب‬‎)",
42490         "ma",
42491         "212",
42492         0
42493       ],
42494       [
42495         "Mozambique (Moçambique)",
42496         "mz",
42497         "258"
42498       ],
42499       [
42500         "Myanmar (Burma) (မြန်မာ)",
42501         "mm",
42502         "95"
42503       ],
42504       [
42505         "Namibia (Namibië)",
42506         "na",
42507         "264"
42508       ],
42509       [
42510         "Nauru",
42511         "nr",
42512         "674"
42513       ],
42514       [
42515         "Nepal (नेपाल)",
42516         "np",
42517         "977"
42518       ],
42519       [
42520         "Netherlands (Nederland)",
42521         "nl",
42522         "31"
42523       ],
42524       [
42525         "New Caledonia (Nouvelle-Calédonie)",
42526         "nc",
42527         "687"
42528       ],
42529       [
42530         "New Zealand",
42531         "nz",
42532         "64"
42533       ],
42534       [
42535         "Nicaragua",
42536         "ni",
42537         "505"
42538       ],
42539       [
42540         "Niger (Nijar)",
42541         "ne",
42542         "227"
42543       ],
42544       [
42545         "Nigeria",
42546         "ng",
42547         "234"
42548       ],
42549       [
42550         "Niue",
42551         "nu",
42552         "683"
42553       ],
42554       [
42555         "Norfolk Island",
42556         "nf",
42557         "672"
42558       ],
42559       [
42560         "North Korea (조선 민주주의 인민 공화국)",
42561         "kp",
42562         "850"
42563       ],
42564       [
42565         "Northern Mariana Islands",
42566         "mp",
42567         "1670"
42568       ],
42569       [
42570         "Norway (Norge)",
42571         "no",
42572         "47",
42573         0
42574       ],
42575       [
42576         "Oman (‫عُمان‬‎)",
42577         "om",
42578         "968"
42579       ],
42580       [
42581         "Pakistan (‫پاکستان‬‎)",
42582         "pk",
42583         "92"
42584       ],
42585       [
42586         "Palau",
42587         "pw",
42588         "680"
42589       ],
42590       [
42591         "Palestine (‫فلسطين‬‎)",
42592         "ps",
42593         "970"
42594       ],
42595       [
42596         "Panama (Panamá)",
42597         "pa",
42598         "507"
42599       ],
42600       [
42601         "Papua New Guinea",
42602         "pg",
42603         "675"
42604       ],
42605       [
42606         "Paraguay",
42607         "py",
42608         "595"
42609       ],
42610       [
42611         "Peru (Perú)",
42612         "pe",
42613         "51"
42614       ],
42615       [
42616         "Philippines",
42617         "ph",
42618         "63"
42619       ],
42620       [
42621         "Poland (Polska)",
42622         "pl",
42623         "48"
42624       ],
42625       [
42626         "Portugal",
42627         "pt",
42628         "351"
42629       ],
42630       [
42631         "Puerto Rico",
42632         "pr",
42633         "1",
42634         3,
42635         ["787", "939"]
42636       ],
42637       [
42638         "Qatar (‫قطر‬‎)",
42639         "qa",
42640         "974"
42641       ],
42642       [
42643         "Réunion (La Réunion)",
42644         "re",
42645         "262",
42646         0
42647       ],
42648       [
42649         "Romania (România)",
42650         "ro",
42651         "40"
42652       ],
42653       [
42654         "Russia (Россия)",
42655         "ru",
42656         "7",
42657         0
42658       ],
42659       [
42660         "Rwanda",
42661         "rw",
42662         "250"
42663       ],
42664       [
42665         "Saint Barthélemy",
42666         "bl",
42667         "590",
42668         1
42669       ],
42670       [
42671         "Saint Helena",
42672         "sh",
42673         "290"
42674       ],
42675       [
42676         "Saint Kitts and Nevis",
42677         "kn",
42678         "1869"
42679       ],
42680       [
42681         "Saint Lucia",
42682         "lc",
42683         "1758"
42684       ],
42685       [
42686         "Saint Martin (Saint-Martin (partie française))",
42687         "mf",
42688         "590",
42689         2
42690       ],
42691       [
42692         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42693         "pm",
42694         "508"
42695       ],
42696       [
42697         "Saint Vincent and the Grenadines",
42698         "vc",
42699         "1784"
42700       ],
42701       [
42702         "Samoa",
42703         "ws",
42704         "685"
42705       ],
42706       [
42707         "San Marino",
42708         "sm",
42709         "378"
42710       ],
42711       [
42712         "São Tomé and Príncipe (São Tomé e Príncipe)",
42713         "st",
42714         "239"
42715       ],
42716       [
42717         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42718         "sa",
42719         "966"
42720       ],
42721       [
42722         "Senegal (Sénégal)",
42723         "sn",
42724         "221"
42725       ],
42726       [
42727         "Serbia (Србија)",
42728         "rs",
42729         "381"
42730       ],
42731       [
42732         "Seychelles",
42733         "sc",
42734         "248"
42735       ],
42736       [
42737         "Sierra Leone",
42738         "sl",
42739         "232"
42740       ],
42741       [
42742         "Singapore",
42743         "sg",
42744         "65"
42745       ],
42746       [
42747         "Sint Maarten",
42748         "sx",
42749         "1721"
42750       ],
42751       [
42752         "Slovakia (Slovensko)",
42753         "sk",
42754         "421"
42755       ],
42756       [
42757         "Slovenia (Slovenija)",
42758         "si",
42759         "386"
42760       ],
42761       [
42762         "Solomon Islands",
42763         "sb",
42764         "677"
42765       ],
42766       [
42767         "Somalia (Soomaaliya)",
42768         "so",
42769         "252"
42770       ],
42771       [
42772         "South Africa",
42773         "za",
42774         "27"
42775       ],
42776       [
42777         "South Korea (대한민국)",
42778         "kr",
42779         "82"
42780       ],
42781       [
42782         "South Sudan (‫جنوب السودان‬‎)",
42783         "ss",
42784         "211"
42785       ],
42786       [
42787         "Spain (España)",
42788         "es",
42789         "34"
42790       ],
42791       [
42792         "Sri Lanka (ශ්‍රී ලංකාව)",
42793         "lk",
42794         "94"
42795       ],
42796       [
42797         "Sudan (‫السودان‬‎)",
42798         "sd",
42799         "249"
42800       ],
42801       [
42802         "Suriname",
42803         "sr",
42804         "597"
42805       ],
42806       [
42807         "Svalbard and Jan Mayen",
42808         "sj",
42809         "47",
42810         1
42811       ],
42812       [
42813         "Swaziland",
42814         "sz",
42815         "268"
42816       ],
42817       [
42818         "Sweden (Sverige)",
42819         "se",
42820         "46"
42821       ],
42822       [
42823         "Switzerland (Schweiz)",
42824         "ch",
42825         "41"
42826       ],
42827       [
42828         "Syria (‫سوريا‬‎)",
42829         "sy",
42830         "963"
42831       ],
42832       [
42833         "Taiwan (台灣)",
42834         "tw",
42835         "886"
42836       ],
42837       [
42838         "Tajikistan",
42839         "tj",
42840         "992"
42841       ],
42842       [
42843         "Tanzania",
42844         "tz",
42845         "255"
42846       ],
42847       [
42848         "Thailand (ไทย)",
42849         "th",
42850         "66"
42851       ],
42852       [
42853         "Timor-Leste",
42854         "tl",
42855         "670"
42856       ],
42857       [
42858         "Togo",
42859         "tg",
42860         "228"
42861       ],
42862       [
42863         "Tokelau",
42864         "tk",
42865         "690"
42866       ],
42867       [
42868         "Tonga",
42869         "to",
42870         "676"
42871       ],
42872       [
42873         "Trinidad and Tobago",
42874         "tt",
42875         "1868"
42876       ],
42877       [
42878         "Tunisia (‫تونس‬‎)",
42879         "tn",
42880         "216"
42881       ],
42882       [
42883         "Turkey (Türkiye)",
42884         "tr",
42885         "90"
42886       ],
42887       [
42888         "Turkmenistan",
42889         "tm",
42890         "993"
42891       ],
42892       [
42893         "Turks and Caicos Islands",
42894         "tc",
42895         "1649"
42896       ],
42897       [
42898         "Tuvalu",
42899         "tv",
42900         "688"
42901       ],
42902       [
42903         "U.S. Virgin Islands",
42904         "vi",
42905         "1340"
42906       ],
42907       [
42908         "Uganda",
42909         "ug",
42910         "256"
42911       ],
42912       [
42913         "Ukraine (Україна)",
42914         "ua",
42915         "380"
42916       ],
42917       [
42918         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42919         "ae",
42920         "971"
42921       ],
42922       [
42923         "United Kingdom",
42924         "gb",
42925         "44",
42926         0
42927       ],
42928       [
42929         "United States",
42930         "us",
42931         "1",
42932         0
42933       ],
42934       [
42935         "Uruguay",
42936         "uy",
42937         "598"
42938       ],
42939       [
42940         "Uzbekistan (Oʻzbekiston)",
42941         "uz",
42942         "998"
42943       ],
42944       [
42945         "Vanuatu",
42946         "vu",
42947         "678"
42948       ],
42949       [
42950         "Vatican City (Città del Vaticano)",
42951         "va",
42952         "39",
42953         1
42954       ],
42955       [
42956         "Venezuela",
42957         "ve",
42958         "58"
42959       ],
42960       [
42961         "Vietnam (Việt Nam)",
42962         "vn",
42963         "84"
42964       ],
42965       [
42966         "Wallis and Futuna (Wallis-et-Futuna)",
42967         "wf",
42968         "681"
42969       ],
42970       [
42971         "Western Sahara (‫الصحراء الغربية‬‎)",
42972         "eh",
42973         "212",
42974         1
42975       ],
42976       [
42977         "Yemen (‫اليمن‬‎)",
42978         "ye",
42979         "967"
42980       ],
42981       [
42982         "Zambia",
42983         "zm",
42984         "260"
42985       ],
42986       [
42987         "Zimbabwe",
42988         "zw",
42989         "263"
42990       ],
42991       [
42992         "Åland Islands",
42993         "ax",
42994         "358",
42995         1
42996       ]
42997   ];
42998   
42999   return d;
43000 }/**
43001 *    This script refer to:
43002 *    Title: International Telephone Input
43003 *    Author: Jack O'Connor
43004 *    Code version:  v12.1.12
43005 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43006 **/
43007
43008 /**
43009  * @class Roo.bootstrap.PhoneInput
43010  * @extends Roo.bootstrap.TriggerField
43011  * An input with International dial-code selection
43012  
43013  * @cfg {String} defaultDialCode default '+852'
43014  * @cfg {Array} preferedCountries default []
43015   
43016  * @constructor
43017  * Create a new PhoneInput.
43018  * @param {Object} config Configuration options
43019  */
43020
43021 Roo.bootstrap.PhoneInput = function(config) {
43022     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43023 };
43024
43025 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43026         
43027         listWidth: undefined,
43028         
43029         selectedClass: 'active',
43030         
43031         invalidClass : "has-warning",
43032         
43033         validClass: 'has-success',
43034         
43035         allowed: '0123456789',
43036         
43037         max_length: 15,
43038         
43039         /**
43040          * @cfg {String} defaultDialCode The default dial code when initializing the input
43041          */
43042         defaultDialCode: '+852',
43043         
43044         /**
43045          * @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
43046          */
43047         preferedCountries: false,
43048         
43049         getAutoCreate : function()
43050         {
43051             var data = Roo.bootstrap.PhoneInputData();
43052             var align = this.labelAlign || this.parentLabelAlign();
43053             var id = Roo.id();
43054             
43055             this.allCountries = [];
43056             this.dialCodeMapping = [];
43057             
43058             for (var i = 0; i < data.length; i++) {
43059               var c = data[i];
43060               this.allCountries[i] = {
43061                 name: c[0],
43062                 iso2: c[1],
43063                 dialCode: c[2],
43064                 priority: c[3] || 0,
43065                 areaCodes: c[4] || null
43066               };
43067               this.dialCodeMapping[c[2]] = {
43068                   name: c[0],
43069                   iso2: c[1],
43070                   priority: c[3] || 0,
43071                   areaCodes: c[4] || null
43072               };
43073             }
43074             
43075             var cfg = {
43076                 cls: 'form-group',
43077                 cn: []
43078             };
43079             
43080             var input =  {
43081                 tag: 'input',
43082                 id : id,
43083                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43084                 maxlength: this.max_length,
43085                 cls : 'form-control tel-input',
43086                 autocomplete: 'new-password'
43087             };
43088             
43089             var hiddenInput = {
43090                 tag: 'input',
43091                 type: 'hidden',
43092                 cls: 'hidden-tel-input'
43093             };
43094             
43095             if (this.name) {
43096                 hiddenInput.name = this.name;
43097             }
43098             
43099             if (this.disabled) {
43100                 input.disabled = true;
43101             }
43102             
43103             var flag_container = {
43104                 tag: 'div',
43105                 cls: 'flag-box',
43106                 cn: [
43107                     {
43108                         tag: 'div',
43109                         cls: 'flag'
43110                     },
43111                     {
43112                         tag: 'div',
43113                         cls: 'caret'
43114                     }
43115                 ]
43116             };
43117             
43118             var box = {
43119                 tag: 'div',
43120                 cls: this.hasFeedback ? 'has-feedback' : '',
43121                 cn: [
43122                     hiddenInput,
43123                     input,
43124                     {
43125                         tag: 'input',
43126                         cls: 'dial-code-holder',
43127                         disabled: true
43128                     }
43129                 ]
43130             };
43131             
43132             var container = {
43133                 cls: 'roo-select2-container input-group',
43134                 cn: [
43135                     flag_container,
43136                     box
43137                 ]
43138             };
43139             
43140             if (this.fieldLabel.length) {
43141                 var indicator = {
43142                     tag: 'i',
43143                     tooltip: 'This field is required'
43144                 };
43145                 
43146                 var label = {
43147                     tag: 'label',
43148                     'for':  id,
43149                     cls: 'control-label',
43150                     cn: []
43151                 };
43152                 
43153                 var label_text = {
43154                     tag: 'span',
43155                     html: this.fieldLabel
43156                 };
43157                 
43158                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43159                 label.cn = [
43160                     indicator,
43161                     label_text
43162                 ];
43163                 
43164                 if(this.indicatorpos == 'right') {
43165                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43166                     label.cn = [
43167                         label_text,
43168                         indicator
43169                     ];
43170                 }
43171                 
43172                 if(align == 'left') {
43173                     container = {
43174                         tag: 'div',
43175                         cn: [
43176                             container
43177                         ]
43178                     };
43179                     
43180                     if(this.labelWidth > 12){
43181                         label.style = "width: " + this.labelWidth + 'px';
43182                     }
43183                     if(this.labelWidth < 13 && this.labelmd == 0){
43184                         this.labelmd = this.labelWidth;
43185                     }
43186                     if(this.labellg > 0){
43187                         label.cls += ' col-lg-' + this.labellg;
43188                         input.cls += ' col-lg-' + (12 - this.labellg);
43189                     }
43190                     if(this.labelmd > 0){
43191                         label.cls += ' col-md-' + this.labelmd;
43192                         container.cls += ' col-md-' + (12 - this.labelmd);
43193                     }
43194                     if(this.labelsm > 0){
43195                         label.cls += ' col-sm-' + this.labelsm;
43196                         container.cls += ' col-sm-' + (12 - this.labelsm);
43197                     }
43198                     if(this.labelxs > 0){
43199                         label.cls += ' col-xs-' + this.labelxs;
43200                         container.cls += ' col-xs-' + (12 - this.labelxs);
43201                     }
43202                 }
43203             }
43204             
43205             cfg.cn = [
43206                 label,
43207                 container
43208             ];
43209             
43210             var settings = this;
43211             
43212             ['xs','sm','md','lg'].map(function(size){
43213                 if (settings[size]) {
43214                     cfg.cls += ' col-' + size + '-' + settings[size];
43215                 }
43216             });
43217             
43218             this.store = new Roo.data.Store({
43219                 proxy : new Roo.data.MemoryProxy({}),
43220                 reader : new Roo.data.JsonReader({
43221                     fields : [
43222                         {
43223                             'name' : 'name',
43224                             'type' : 'string'
43225                         },
43226                         {
43227                             'name' : 'iso2',
43228                             'type' : 'string'
43229                         },
43230                         {
43231                             'name' : 'dialCode',
43232                             'type' : 'string'
43233                         },
43234                         {
43235                             'name' : 'priority',
43236                             'type' : 'string'
43237                         },
43238                         {
43239                             'name' : 'areaCodes',
43240                             'type' : 'string'
43241                         }
43242                     ]
43243                 })
43244             });
43245             
43246             if(!this.preferedCountries) {
43247                 this.preferedCountries = [
43248                     'hk',
43249                     'gb',
43250                     'us'
43251                 ];
43252             }
43253             
43254             var p = this.preferedCountries.reverse();
43255             
43256             if(p) {
43257                 for (var i = 0; i < p.length; i++) {
43258                     for (var j = 0; j < this.allCountries.length; j++) {
43259                         if(this.allCountries[j].iso2 == p[i]) {
43260                             var t = this.allCountries[j];
43261                             this.allCountries.splice(j,1);
43262                             this.allCountries.unshift(t);
43263                         }
43264                     } 
43265                 }
43266             }
43267             
43268             this.store.proxy.data = {
43269                 success: true,
43270                 data: this.allCountries
43271             };
43272             
43273             return cfg;
43274         },
43275         
43276         initEvents : function()
43277         {
43278             this.createList();
43279             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43280             
43281             this.indicator = this.indicatorEl();
43282             this.flag = this.flagEl();
43283             this.dialCodeHolder = this.dialCodeHolderEl();
43284             
43285             this.trigger = this.el.select('div.flag-box',true).first();
43286             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43287             
43288             var _this = this;
43289             
43290             (function(){
43291                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43292                 _this.list.setWidth(lw);
43293             }).defer(100);
43294             
43295             this.list.on('mouseover', this.onViewOver, this);
43296             this.list.on('mousemove', this.onViewMove, this);
43297             this.inputEl().on("keyup", this.onKeyUp, this);
43298             this.inputEl().on("keypress", this.onKeyPress, this);
43299             
43300             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43301
43302             this.view = new Roo.View(this.list, this.tpl, {
43303                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43304             });
43305             
43306             this.view.on('click', this.onViewClick, this);
43307             this.setValue(this.defaultDialCode);
43308         },
43309         
43310         onTriggerClick : function(e)
43311         {
43312             Roo.log('trigger click');
43313             if(this.disabled){
43314                 return;
43315             }
43316             
43317             if(this.isExpanded()){
43318                 this.collapse();
43319                 this.hasFocus = false;
43320             }else {
43321                 this.store.load({});
43322                 this.hasFocus = true;
43323                 this.expand();
43324             }
43325         },
43326         
43327         isExpanded : function()
43328         {
43329             return this.list.isVisible();
43330         },
43331         
43332         collapse : function()
43333         {
43334             if(!this.isExpanded()){
43335                 return;
43336             }
43337             this.list.hide();
43338             Roo.get(document).un('mousedown', this.collapseIf, this);
43339             Roo.get(document).un('mousewheel', this.collapseIf, this);
43340             this.fireEvent('collapse', this);
43341             this.validate();
43342         },
43343         
43344         expand : function()
43345         {
43346             Roo.log('expand');
43347
43348             if(this.isExpanded() || !this.hasFocus){
43349                 return;
43350             }
43351             
43352             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43353             this.list.setWidth(lw);
43354             
43355             this.list.show();
43356             this.restrictHeight();
43357             
43358             Roo.get(document).on('mousedown', this.collapseIf, this);
43359             Roo.get(document).on('mousewheel', this.collapseIf, this);
43360             
43361             this.fireEvent('expand', this);
43362         },
43363         
43364         restrictHeight : function()
43365         {
43366             this.list.alignTo(this.inputEl(), this.listAlign);
43367             this.list.alignTo(this.inputEl(), this.listAlign);
43368         },
43369         
43370         onViewOver : function(e, t)
43371         {
43372             if(this.inKeyMode){
43373                 return;
43374             }
43375             var item = this.view.findItemFromChild(t);
43376             
43377             if(item){
43378                 var index = this.view.indexOf(item);
43379                 this.select(index, false);
43380             }
43381         },
43382
43383         // private
43384         onViewClick : function(view, doFocus, el, e)
43385         {
43386             var index = this.view.getSelectedIndexes()[0];
43387             
43388             var r = this.store.getAt(index);
43389             
43390             if(r){
43391                 this.onSelect(r, index);
43392             }
43393             if(doFocus !== false && !this.blockFocus){
43394                 this.inputEl().focus();
43395             }
43396         },
43397         
43398         onViewMove : function(e, t)
43399         {
43400             this.inKeyMode = false;
43401         },
43402         
43403         select : function(index, scrollIntoView)
43404         {
43405             this.selectedIndex = index;
43406             this.view.select(index);
43407             if(scrollIntoView !== false){
43408                 var el = this.view.getNode(index);
43409                 if(el){
43410                     this.list.scrollChildIntoView(el, false);
43411                 }
43412             }
43413         },
43414         
43415         createList : function()
43416         {
43417             this.list = Roo.get(document.body).createChild({
43418                 tag: 'ul',
43419                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43420                 style: 'display:none'
43421             });
43422             
43423             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43424         },
43425         
43426         collapseIf : function(e)
43427         {
43428             var in_combo  = e.within(this.el);
43429             var in_list =  e.within(this.list);
43430             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43431             
43432             if (in_combo || in_list || is_list) {
43433                 return;
43434             }
43435             this.collapse();
43436         },
43437         
43438         onSelect : function(record, index)
43439         {
43440             if(this.fireEvent('beforeselect', this, record, index) !== false){
43441                 
43442                 this.setFlagClass(record.data.iso2);
43443                 this.setDialCode(record.data.dialCode);
43444                 this.hasFocus = false;
43445                 this.collapse();
43446                 this.fireEvent('select', this, record, index);
43447             }
43448         },
43449         
43450         flagEl : function()
43451         {
43452             var flag = this.el.select('div.flag',true).first();
43453             if(!flag){
43454                 return false;
43455             }
43456             return flag;
43457         },
43458         
43459         dialCodeHolderEl : function()
43460         {
43461             var d = this.el.select('input.dial-code-holder',true).first();
43462             if(!d){
43463                 return false;
43464             }
43465             return d;
43466         },
43467         
43468         setDialCode : function(v)
43469         {
43470             this.dialCodeHolder.dom.value = '+'+v;
43471         },
43472         
43473         setFlagClass : function(n)
43474         {
43475             this.flag.dom.className = 'flag '+n;
43476         },
43477         
43478         getValue : function()
43479         {
43480             var v = this.inputEl().getValue();
43481             if(this.dialCodeHolder) {
43482                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43483             }
43484             return v;
43485         },
43486         
43487         setValue : function(v)
43488         {
43489             var d = this.getDialCode(v);
43490             
43491             //invalid dial code
43492             if(v.length == 0 || !d || d.length == 0) {
43493                 if(this.rendered){
43494                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43495                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43496                 }
43497                 return;
43498             }
43499             
43500             //valid dial code
43501             this.setFlagClass(this.dialCodeMapping[d].iso2);
43502             this.setDialCode(d);
43503             this.inputEl().dom.value = v.replace('+'+d,'');
43504             this.hiddenEl().dom.value = this.getValue();
43505             
43506             this.validate();
43507         },
43508         
43509         getDialCode : function(v)
43510         {
43511             v = v ||  '';
43512             
43513             if (v.length == 0) {
43514                 return this.dialCodeHolder.dom.value;
43515             }
43516             
43517             var dialCode = "";
43518             if (v.charAt(0) != "+") {
43519                 return false;
43520             }
43521             var numericChars = "";
43522             for (var i = 1; i < v.length; i++) {
43523               var c = v.charAt(i);
43524               if (!isNaN(c)) {
43525                 numericChars += c;
43526                 if (this.dialCodeMapping[numericChars]) {
43527                   dialCode = v.substr(1, i);
43528                 }
43529                 if (numericChars.length == 4) {
43530                   break;
43531                 }
43532               }
43533             }
43534             return dialCode;
43535         },
43536         
43537         reset : function()
43538         {
43539             this.setValue(this.defaultDialCode);
43540             this.validate();
43541         },
43542         
43543         hiddenEl : function()
43544         {
43545             return this.el.select('input.hidden-tel-input',true).first();
43546         },
43547         
43548         // after setting val
43549         onKeyUp : function(e){
43550             this.setValue(this.getValue());
43551         },
43552         
43553         onKeyPress : function(e){
43554             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43555                 e.stopEvent();
43556             }
43557         }
43558         
43559 });
43560 /**
43561  * @class Roo.bootstrap.MoneyField
43562  * @extends Roo.bootstrap.ComboBox
43563  * Bootstrap MoneyField class
43564  * 
43565  * @constructor
43566  * Create a new MoneyField.
43567  * @param {Object} config Configuration options
43568  */
43569
43570 Roo.bootstrap.MoneyField = function(config) {
43571     
43572     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43573     
43574 };
43575
43576 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43577     
43578     /**
43579      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43580      */
43581     allowDecimals : true,
43582     /**
43583      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43584      */
43585     decimalSeparator : ".",
43586     /**
43587      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43588      */
43589     decimalPrecision : 0,
43590     /**
43591      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43592      */
43593     allowNegative : true,
43594     /**
43595      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43596      */
43597     allowZero: true,
43598     /**
43599      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43600      */
43601     minValue : Number.NEGATIVE_INFINITY,
43602     /**
43603      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43604      */
43605     maxValue : Number.MAX_VALUE,
43606     /**
43607      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43608      */
43609     minText : "The minimum value for this field is {0}",
43610     /**
43611      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43612      */
43613     maxText : "The maximum value for this field is {0}",
43614     /**
43615      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43616      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43617      */
43618     nanText : "{0} is not a valid number",
43619     /**
43620      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43621      */
43622     castInt : true,
43623     /**
43624      * @cfg {String} defaults currency of the MoneyField
43625      * value should be in lkey
43626      */
43627     defaultCurrency : false,
43628     /**
43629      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43630      */
43631     thousandsDelimiter : false,
43632     /**
43633      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43634      */
43635     max_length: false,
43636     
43637     inputlg : 9,
43638     inputmd : 9,
43639     inputsm : 9,
43640     inputxs : 6,
43641     
43642     store : false,
43643     
43644     getAutoCreate : function()
43645     {
43646         var align = this.labelAlign || this.parentLabelAlign();
43647         
43648         var id = Roo.id();
43649
43650         var cfg = {
43651             cls: 'form-group',
43652             cn: []
43653         };
43654
43655         var input =  {
43656             tag: 'input',
43657             id : id,
43658             cls : 'form-control roo-money-amount-input',
43659             autocomplete: 'new-password'
43660         };
43661         
43662         var hiddenInput = {
43663             tag: 'input',
43664             type: 'hidden',
43665             id: Roo.id(),
43666             cls: 'hidden-number-input'
43667         };
43668         
43669         if(this.max_length) {
43670             input.maxlength = this.max_length; 
43671         }
43672         
43673         if (this.name) {
43674             hiddenInput.name = this.name;
43675         }
43676
43677         if (this.disabled) {
43678             input.disabled = true;
43679         }
43680
43681         var clg = 12 - this.inputlg;
43682         var cmd = 12 - this.inputmd;
43683         var csm = 12 - this.inputsm;
43684         var cxs = 12 - this.inputxs;
43685         
43686         var container = {
43687             tag : 'div',
43688             cls : 'row roo-money-field',
43689             cn : [
43690                 {
43691                     tag : 'div',
43692                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43693                     cn : [
43694                         {
43695                             tag : 'div',
43696                             cls: 'roo-select2-container input-group',
43697                             cn: [
43698                                 {
43699                                     tag : 'input',
43700                                     cls : 'form-control roo-money-currency-input',
43701                                     autocomplete: 'new-password',
43702                                     readOnly : 1,
43703                                     name : this.currencyName
43704                                 },
43705                                 {
43706                                     tag :'span',
43707                                     cls : 'input-group-addon',
43708                                     cn : [
43709                                         {
43710                                             tag: 'span',
43711                                             cls: 'caret'
43712                                         }
43713                                     ]
43714                                 }
43715                             ]
43716                         }
43717                     ]
43718                 },
43719                 {
43720                     tag : 'div',
43721                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43722                     cn : [
43723                         {
43724                             tag: 'div',
43725                             cls: this.hasFeedback ? 'has-feedback' : '',
43726                             cn: [
43727                                 input
43728                             ]
43729                         }
43730                     ]
43731                 }
43732             ]
43733             
43734         };
43735         
43736         if (this.fieldLabel.length) {
43737             var indicator = {
43738                 tag: 'i',
43739                 tooltip: 'This field is required'
43740             };
43741
43742             var label = {
43743                 tag: 'label',
43744                 'for':  id,
43745                 cls: 'control-label',
43746                 cn: []
43747             };
43748
43749             var label_text = {
43750                 tag: 'span',
43751                 html: this.fieldLabel
43752             };
43753
43754             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43755             label.cn = [
43756                 indicator,
43757                 label_text
43758             ];
43759
43760             if(this.indicatorpos == 'right') {
43761                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43762                 label.cn = [
43763                     label_text,
43764                     indicator
43765                 ];
43766             }
43767
43768             if(align == 'left') {
43769                 container = {
43770                     tag: 'div',
43771                     cn: [
43772                         container
43773                     ]
43774                 };
43775
43776                 if(this.labelWidth > 12){
43777                     label.style = "width: " + this.labelWidth + 'px';
43778                 }
43779                 if(this.labelWidth < 13 && this.labelmd == 0){
43780                     this.labelmd = this.labelWidth;
43781                 }
43782                 if(this.labellg > 0){
43783                     label.cls += ' col-lg-' + this.labellg;
43784                     input.cls += ' col-lg-' + (12 - this.labellg);
43785                 }
43786                 if(this.labelmd > 0){
43787                     label.cls += ' col-md-' + this.labelmd;
43788                     container.cls += ' col-md-' + (12 - this.labelmd);
43789                 }
43790                 if(this.labelsm > 0){
43791                     label.cls += ' col-sm-' + this.labelsm;
43792                     container.cls += ' col-sm-' + (12 - this.labelsm);
43793                 }
43794                 if(this.labelxs > 0){
43795                     label.cls += ' col-xs-' + this.labelxs;
43796                     container.cls += ' col-xs-' + (12 - this.labelxs);
43797                 }
43798             }
43799         }
43800
43801         cfg.cn = [
43802             label,
43803             container,
43804             hiddenInput
43805         ];
43806         
43807         var settings = this;
43808
43809         ['xs','sm','md','lg'].map(function(size){
43810             if (settings[size]) {
43811                 cfg.cls += ' col-' + size + '-' + settings[size];
43812             }
43813         });
43814         
43815         return cfg;
43816     },
43817     
43818     initEvents : function()
43819     {
43820         this.indicator = this.indicatorEl();
43821         
43822         this.initCurrencyEvent();
43823         
43824         this.initNumberEvent();
43825     },
43826     
43827     initCurrencyEvent : function()
43828     {
43829         if (!this.store) {
43830             throw "can not find store for combo";
43831         }
43832         
43833         this.store = Roo.factory(this.store, Roo.data);
43834         this.store.parent = this;
43835         
43836         this.createList();
43837         
43838         this.triggerEl = this.el.select('.input-group-addon', true).first();
43839         
43840         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43841         
43842         var _this = this;
43843         
43844         (function(){
43845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43846             _this.list.setWidth(lw);
43847         }).defer(100);
43848         
43849         this.list.on('mouseover', this.onViewOver, this);
43850         this.list.on('mousemove', this.onViewMove, this);
43851         this.list.on('scroll', this.onViewScroll, this);
43852         
43853         if(!this.tpl){
43854             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43855         }
43856         
43857         this.view = new Roo.View(this.list, this.tpl, {
43858             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43859         });
43860         
43861         this.view.on('click', this.onViewClick, this);
43862         
43863         this.store.on('beforeload', this.onBeforeLoad, this);
43864         this.store.on('load', this.onLoad, this);
43865         this.store.on('loadexception', this.onLoadException, this);
43866         
43867         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43868             "up" : function(e){
43869                 this.inKeyMode = true;
43870                 this.selectPrev();
43871             },
43872
43873             "down" : function(e){
43874                 if(!this.isExpanded()){
43875                     this.onTriggerClick();
43876                 }else{
43877                     this.inKeyMode = true;
43878                     this.selectNext();
43879                 }
43880             },
43881
43882             "enter" : function(e){
43883                 this.collapse();
43884                 
43885                 if(this.fireEvent("specialkey", this, e)){
43886                     this.onViewClick(false);
43887                 }
43888                 
43889                 return true;
43890             },
43891
43892             "esc" : function(e){
43893                 this.collapse();
43894             },
43895
43896             "tab" : function(e){
43897                 this.collapse();
43898                 
43899                 if(this.fireEvent("specialkey", this, e)){
43900                     this.onViewClick(false);
43901                 }
43902                 
43903                 return true;
43904             },
43905
43906             scope : this,
43907
43908             doRelay : function(foo, bar, hname){
43909                 if(hname == 'down' || this.scope.isExpanded()){
43910                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43911                 }
43912                 return true;
43913             },
43914
43915             forceKeyDown: true
43916         });
43917         
43918         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43919         
43920     },
43921     
43922     initNumberEvent : function(e)
43923     {
43924         this.inputEl().on("keydown" , this.fireKey,  this);
43925         this.inputEl().on("focus", this.onFocus,  this);
43926         this.inputEl().on("blur", this.onBlur,  this);
43927         
43928         this.inputEl().relayEvent('keyup', this);
43929         
43930         if(this.indicator){
43931             this.indicator.addClass('invisible');
43932         }
43933  
43934         this.originalValue = this.getValue();
43935         
43936         if(this.validationEvent == 'keyup'){
43937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43938             this.inputEl().on('keyup', this.filterValidation, this);
43939         }
43940         else if(this.validationEvent !== false){
43941             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43942         }
43943         
43944         if(this.selectOnFocus){
43945             this.on("focus", this.preFocus, this);
43946             
43947         }
43948         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43949             this.inputEl().on("keypress", this.filterKeys, this);
43950         } else {
43951             this.inputEl().relayEvent('keypress', this);
43952         }
43953         
43954         var allowed = "0123456789";
43955         
43956         if(this.allowDecimals){
43957             allowed += this.decimalSeparator;
43958         }
43959         
43960         if(this.allowNegative){
43961             allowed += "-";
43962         }
43963         
43964         if(this.thousandsDelimiter) {
43965             allowed += ",";
43966         }
43967         
43968         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43969         
43970         var keyPress = function(e){
43971             
43972             var k = e.getKey();
43973             
43974             var c = e.getCharCode();
43975             
43976             if(
43977                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43978                     allowed.indexOf(String.fromCharCode(c)) === -1
43979             ){
43980                 e.stopEvent();
43981                 return;
43982             }
43983             
43984             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43985                 return;
43986             }
43987             
43988             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43989                 e.stopEvent();
43990             }
43991         };
43992         
43993         this.inputEl().on("keypress", keyPress, this);
43994         
43995     },
43996     
43997     onTriggerClick : function(e)
43998     {   
43999         if(this.disabled){
44000             return;
44001         }
44002         
44003         this.page = 0;
44004         this.loadNext = false;
44005         
44006         if(this.isExpanded()){
44007             this.collapse();
44008             return;
44009         }
44010         
44011         this.hasFocus = true;
44012         
44013         if(this.triggerAction == 'all') {
44014             this.doQuery(this.allQuery, true);
44015             return;
44016         }
44017         
44018         this.doQuery(this.getRawValue());
44019     },
44020     
44021     getCurrency : function()
44022     {   
44023         var v = this.currencyEl().getValue();
44024         
44025         return v;
44026     },
44027     
44028     restrictHeight : function()
44029     {
44030         this.list.alignTo(this.currencyEl(), this.listAlign);
44031         this.list.alignTo(this.currencyEl(), this.listAlign);
44032     },
44033     
44034     onViewClick : function(view, doFocus, el, e)
44035     {
44036         var index = this.view.getSelectedIndexes()[0];
44037         
44038         var r = this.store.getAt(index);
44039         
44040         if(r){
44041             this.onSelect(r, index);
44042         }
44043     },
44044     
44045     onSelect : function(record, index){
44046         
44047         if(this.fireEvent('beforeselect', this, record, index) !== false){
44048         
44049             this.setFromCurrencyData(index > -1 ? record.data : false);
44050             
44051             this.collapse();
44052             
44053             this.fireEvent('select', this, record, index);
44054         }
44055     },
44056     
44057     setFromCurrencyData : function(o)
44058     {
44059         var currency = '';
44060         
44061         this.lastCurrency = o;
44062         
44063         if (this.currencyField) {
44064             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44065         } else {
44066             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44067         }
44068         
44069         this.lastSelectionText = currency;
44070         
44071         //setting default currency
44072         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44073             this.setCurrency(this.defaultCurrency);
44074             return;
44075         }
44076         
44077         this.setCurrency(currency);
44078     },
44079     
44080     setFromData : function(o)
44081     {
44082         var c = {};
44083         
44084         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44085         
44086         this.setFromCurrencyData(c);
44087         
44088         var value = '';
44089         
44090         if (this.name) {
44091             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44092         } else {
44093             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44094         }
44095         
44096         this.setValue(value);
44097         
44098     },
44099     
44100     setCurrency : function(v)
44101     {   
44102         this.currencyValue = v;
44103         
44104         if(this.rendered){
44105             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44106             this.validate();
44107         }
44108     },
44109     
44110     setValue : function(v)
44111     {
44112         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44113         
44114         this.value = v;
44115         
44116         if(this.rendered){
44117             
44118             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44119             
44120             this.inputEl().dom.value = (v == '') ? '' :
44121                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44122             
44123             if(!this.allowZero && v === '0') {
44124                 this.hiddenEl().dom.value = '';
44125                 this.inputEl().dom.value = '';
44126             }
44127             
44128             this.validate();
44129         }
44130     },
44131     
44132     getRawValue : function()
44133     {
44134         var v = this.inputEl().getValue();
44135         
44136         return v;
44137     },
44138     
44139     getValue : function()
44140     {
44141         return this.fixPrecision(this.parseValue(this.getRawValue()));
44142     },
44143     
44144     parseValue : function(value)
44145     {
44146         if(this.thousandsDelimiter) {
44147             value += "";
44148             r = new RegExp(",", "g");
44149             value = value.replace(r, "");
44150         }
44151         
44152         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44153         return isNaN(value) ? '' : value;
44154         
44155     },
44156     
44157     fixPrecision : function(value)
44158     {
44159         if(this.thousandsDelimiter) {
44160             value += "";
44161             r = new RegExp(",", "g");
44162             value = value.replace(r, "");
44163         }
44164         
44165         var nan = isNaN(value);
44166         
44167         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44168             return nan ? '' : value;
44169         }
44170         return parseFloat(value).toFixed(this.decimalPrecision);
44171     },
44172     
44173     decimalPrecisionFcn : function(v)
44174     {
44175         return Math.floor(v);
44176     },
44177     
44178     validateValue : function(value)
44179     {
44180         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44181             return false;
44182         }
44183         
44184         var num = this.parseValue(value);
44185         
44186         if(isNaN(num)){
44187             this.markInvalid(String.format(this.nanText, value));
44188             return false;
44189         }
44190         
44191         if(num < this.minValue){
44192             this.markInvalid(String.format(this.minText, this.minValue));
44193             return false;
44194         }
44195         
44196         if(num > this.maxValue){
44197             this.markInvalid(String.format(this.maxText, this.maxValue));
44198             return false;
44199         }
44200         
44201         return true;
44202     },
44203     
44204     validate : function()
44205     {
44206         if(this.disabled || this.allowBlank){
44207             this.markValid();
44208             return true;
44209         }
44210         
44211         var currency = this.getCurrency();
44212         
44213         if(this.validateValue(this.getRawValue()) && currency.length){
44214             this.markValid();
44215             return true;
44216         }
44217         
44218         this.markInvalid();
44219         return false;
44220     },
44221     
44222     getName: function()
44223     {
44224         return this.name;
44225     },
44226     
44227     beforeBlur : function()
44228     {
44229         if(!this.castInt){
44230             return;
44231         }
44232         
44233         var v = this.parseValue(this.getRawValue());
44234         
44235         if(v || v == 0){
44236             this.setValue(v);
44237         }
44238     },
44239     
44240     onBlur : function()
44241     {
44242         this.beforeBlur();
44243         
44244         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44245             //this.el.removeClass(this.focusClass);
44246         }
44247         
44248         this.hasFocus = false;
44249         
44250         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44251             this.validate();
44252         }
44253         
44254         var v = this.getValue();
44255         
44256         if(String(v) !== String(this.startValue)){
44257             this.fireEvent('change', this, v, this.startValue);
44258         }
44259         
44260         this.fireEvent("blur", this);
44261     },
44262     
44263     inputEl : function()
44264     {
44265         return this.el.select('.roo-money-amount-input', true).first();
44266     },
44267     
44268     currencyEl : function()
44269     {
44270         return this.el.select('.roo-money-currency-input', true).first();
44271     },
44272     
44273     hiddenEl : function()
44274     {
44275         return this.el.select('input.hidden-number-input',true).first();
44276     }
44277     
44278 });/**
44279  * @class Roo.bootstrap.BezierSignature
44280  * @extends Roo.bootstrap.Component
44281  * Bootstrap BezierSignature class
44282  * This script refer to:
44283  *    Title: Signature Pad
44284  *    Author: szimek
44285  *    Availability: https://github.com/szimek/signature_pad
44286  *
44287  * @constructor
44288  * Create a new BezierSignature
44289  * @param {Object} config The config object
44290  */
44291
44292 Roo.bootstrap.BezierSignature = function(config){
44293     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44294     this.addEvents({
44295         "resize" : true
44296     });
44297 };
44298
44299 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44300 {
44301      
44302     curve_data: [],
44303     
44304     is_empty: true,
44305     
44306     mouse_btn_down: true,
44307     
44308     /**
44309      * @cfg {int} canvas height
44310      */
44311     canvas_height: '200px',
44312     
44313     /**
44314      * @cfg {float|function} Radius of a single dot.
44315      */ 
44316     dot_size: false,
44317     
44318     /**
44319      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44320      */
44321     min_width: 0.5,
44322     
44323     /**
44324      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44325      */
44326     max_width: 2.5,
44327     
44328     /**
44329      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44330      */
44331     throttle: 16,
44332     
44333     /**
44334      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44335      */
44336     min_distance: 5,
44337     
44338     /**
44339      * @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.
44340      */
44341     bg_color: 'rgba(0, 0, 0, 0)',
44342     
44343     /**
44344      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44345      */
44346     dot_color: 'black',
44347     
44348     /**
44349      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44350      */ 
44351     velocity_filter_weight: 0.7,
44352     
44353     /**
44354      * @cfg {function} Callback when stroke begin. 
44355      */
44356     onBegin: false,
44357     
44358     /**
44359      * @cfg {function} Callback when stroke end.
44360      */
44361     onEnd: false,
44362     
44363     getAutoCreate : function()
44364     {
44365         var cls = 'roo-signature column';
44366         
44367         if(this.cls){
44368             cls += ' ' + this.cls;
44369         }
44370         
44371         var col_sizes = [
44372             'lg',
44373             'md',
44374             'sm',
44375             'xs'
44376         ];
44377         
44378         for(var i = 0; i < col_sizes.length; i++) {
44379             if(this[col_sizes[i]]) {
44380                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44381             }
44382         }
44383         
44384         var cfg = {
44385             tag: 'div',
44386             cls: cls,
44387             cn: [
44388                 {
44389                     tag: 'div',
44390                     cls: 'roo-signature-body',
44391                     cn: [
44392                         {
44393                             tag: 'canvas',
44394                             cls: 'roo-signature-body-canvas',
44395                             height: this.canvas_height,
44396                             width: this.canvas_width
44397                         }
44398                     ]
44399                 },
44400                 {
44401                     tag: 'input',
44402                     type: 'file',
44403                     style: 'display: none'
44404                 }
44405             ]
44406         };
44407         
44408         return cfg;
44409     },
44410     
44411     initEvents: function() 
44412     {
44413         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44414         
44415         var canvas = this.canvasEl();
44416         
44417         // mouse && touch event swapping...
44418         canvas.dom.style.touchAction = 'none';
44419         canvas.dom.style.msTouchAction = 'none';
44420         
44421         this.mouse_btn_down = false;
44422         canvas.on('mousedown', this._handleMouseDown, this);
44423         canvas.on('mousemove', this._handleMouseMove, this);
44424         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44425         
44426         if (window.PointerEvent) {
44427             canvas.on('pointerdown', this._handleMouseDown, this);
44428             canvas.on('pointermove', this._handleMouseMove, this);
44429             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44430         }
44431         
44432         if ('ontouchstart' in window) {
44433             canvas.on('touchstart', this._handleTouchStart, this);
44434             canvas.on('touchmove', this._handleTouchMove, this);
44435             canvas.on('touchend', this._handleTouchEnd, this);
44436         }
44437         
44438         Roo.EventManager.onWindowResize(this.resize, this, true);
44439         
44440         // file input event
44441         this.fileEl().on('change', this.uploadImage, this);
44442         
44443         this.clear();
44444         
44445         this.resize();
44446     },
44447     
44448     resize: function(){
44449         
44450         var canvas = this.canvasEl().dom;
44451         var ctx = this.canvasElCtx();
44452         var img_data = false;
44453         
44454         if(canvas.width > 0) {
44455             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44456         }
44457         // setting canvas width will clean img data
44458         canvas.width = 0;
44459         
44460         var style = window.getComputedStyle ? 
44461             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44462             
44463         var padding_left = parseInt(style.paddingLeft) || 0;
44464         var padding_right = parseInt(style.paddingRight) || 0;
44465         
44466         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44467         
44468         if(img_data) {
44469             ctx.putImageData(img_data, 0, 0);
44470         }
44471     },
44472     
44473     _handleMouseDown: function(e)
44474     {
44475         if (e.browserEvent.which === 1) {
44476             this.mouse_btn_down = true;
44477             this.strokeBegin(e);
44478         }
44479     },
44480     
44481     _handleMouseMove: function (e)
44482     {
44483         if (this.mouse_btn_down) {
44484             this.strokeMoveUpdate(e);
44485         }
44486     },
44487     
44488     _handleMouseUp: function (e)
44489     {
44490         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44491             this.mouse_btn_down = false;
44492             this.strokeEnd(e);
44493         }
44494     },
44495     
44496     _handleTouchStart: function (e) {
44497         
44498         e.preventDefault();
44499         if (e.browserEvent.targetTouches.length === 1) {
44500             // var touch = e.browserEvent.changedTouches[0];
44501             // this.strokeBegin(touch);
44502             
44503              this.strokeBegin(e); // assume e catching the correct xy...
44504         }
44505     },
44506     
44507     _handleTouchMove: function (e) {
44508         e.preventDefault();
44509         // var touch = event.targetTouches[0];
44510         // _this._strokeMoveUpdate(touch);
44511         this.strokeMoveUpdate(e);
44512     },
44513     
44514     _handleTouchEnd: function (e) {
44515         var wasCanvasTouched = e.target === this.canvasEl().dom;
44516         if (wasCanvasTouched) {
44517             e.preventDefault();
44518             // var touch = event.changedTouches[0];
44519             // _this._strokeEnd(touch);
44520             this.strokeEnd(e);
44521         }
44522     },
44523     
44524     reset: function () {
44525         this._lastPoints = [];
44526         this._lastVelocity = 0;
44527         this._lastWidth = (this.min_width + this.max_width) / 2;
44528         this.canvasElCtx().fillStyle = this.dot_color;
44529     },
44530     
44531     strokeMoveUpdate: function(e)
44532     {
44533         this.strokeUpdate(e);
44534         
44535         if (this.throttle) {
44536             this.throttleStroke(this.strokeUpdate, this.throttle);
44537         }
44538         else {
44539             this.strokeUpdate(e);
44540         }
44541     },
44542     
44543     strokeBegin: function(e)
44544     {
44545         var newPointGroup = {
44546             color: this.dot_color,
44547             points: []
44548         };
44549         
44550         if (typeof this.onBegin === 'function') {
44551             this.onBegin(e);
44552         }
44553         
44554         this.curve_data.push(newPointGroup);
44555         this.reset();
44556         this.strokeUpdate(e);
44557     },
44558     
44559     strokeUpdate: function(e)
44560     {
44561         var rect = this.canvasEl().dom.getBoundingClientRect();
44562         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44563         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44564         var lastPoints = lastPointGroup.points;
44565         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44566         var isLastPointTooClose = lastPoint
44567             ? point.distanceTo(lastPoint) <= this.min_distance
44568             : false;
44569         var color = lastPointGroup.color;
44570         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44571             var curve = this.addPoint(point);
44572             if (!lastPoint) {
44573                 this.drawDot({color: color, point: point});
44574             }
44575             else if (curve) {
44576                 this.drawCurve({color: color, curve: curve});
44577             }
44578             lastPoints.push({
44579                 time: point.time,
44580                 x: point.x,
44581                 y: point.y
44582             });
44583         }
44584     },
44585     
44586     strokeEnd: function(e)
44587     {
44588         this.strokeUpdate(e);
44589         if (typeof this.onEnd === 'function') {
44590             this.onEnd(e);
44591         }
44592     },
44593     
44594     addPoint:  function (point) {
44595         var _lastPoints = this._lastPoints;
44596         _lastPoints.push(point);
44597         if (_lastPoints.length > 2) {
44598             if (_lastPoints.length === 3) {
44599                 _lastPoints.unshift(_lastPoints[0]);
44600             }
44601             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44602             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44603             _lastPoints.shift();
44604             return curve;
44605         }
44606         return null;
44607     },
44608     
44609     calculateCurveWidths: function (startPoint, endPoint) {
44610         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44611             (1 - this.velocity_filter_weight) * this._lastVelocity;
44612
44613         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44614         var widths = {
44615             end: newWidth,
44616             start: this._lastWidth
44617         };
44618         
44619         this._lastVelocity = velocity;
44620         this._lastWidth = newWidth;
44621         return widths;
44622     },
44623     
44624     drawDot: function (_a) {
44625         var color = _a.color, point = _a.point;
44626         var ctx = this.canvasElCtx();
44627         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44628         ctx.beginPath();
44629         this.drawCurveSegment(point.x, point.y, width);
44630         ctx.closePath();
44631         ctx.fillStyle = color;
44632         ctx.fill();
44633     },
44634     
44635     drawCurve: function (_a) {
44636         var color = _a.color, curve = _a.curve;
44637         var ctx = this.canvasElCtx();
44638         var widthDelta = curve.endWidth - curve.startWidth;
44639         var drawSteps = Math.floor(curve.length()) * 2;
44640         ctx.beginPath();
44641         ctx.fillStyle = color;
44642         for (var i = 0; i < drawSteps; i += 1) {
44643         var t = i / drawSteps;
44644         var tt = t * t;
44645         var ttt = tt * t;
44646         var u = 1 - t;
44647         var uu = u * u;
44648         var uuu = uu * u;
44649         var x = uuu * curve.startPoint.x;
44650         x += 3 * uu * t * curve.control1.x;
44651         x += 3 * u * tt * curve.control2.x;
44652         x += ttt * curve.endPoint.x;
44653         var y = uuu * curve.startPoint.y;
44654         y += 3 * uu * t * curve.control1.y;
44655         y += 3 * u * tt * curve.control2.y;
44656         y += ttt * curve.endPoint.y;
44657         var width = curve.startWidth + ttt * widthDelta;
44658         this.drawCurveSegment(x, y, width);
44659         }
44660         ctx.closePath();
44661         ctx.fill();
44662     },
44663     
44664     drawCurveSegment: function (x, y, width) {
44665         var ctx = this.canvasElCtx();
44666         ctx.moveTo(x, y);
44667         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44668         this.is_empty = false;
44669     },
44670     
44671     clear: function()
44672     {
44673         var ctx = this.canvasElCtx();
44674         var canvas = this.canvasEl().dom;
44675         ctx.fillStyle = this.bg_color;
44676         ctx.clearRect(0, 0, canvas.width, canvas.height);
44677         ctx.fillRect(0, 0, canvas.width, canvas.height);
44678         this.curve_data = [];
44679         this.reset();
44680         this.is_empty = true;
44681     },
44682     
44683     fileEl: function()
44684     {
44685         return  this.el.select('input',true).first();
44686     },
44687     
44688     canvasEl: function()
44689     {
44690         return this.el.select('canvas',true).first();
44691     },
44692     
44693     canvasElCtx: function()
44694     {
44695         return this.el.select('canvas',true).first().dom.getContext('2d');
44696     },
44697     
44698     getImage: function(type)
44699     {
44700         if(this.is_empty) {
44701             return false;
44702         }
44703         
44704         // encryption ?
44705         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44706     },
44707     
44708     drawFromImage: function(img_src)
44709     {
44710         var img = new Image();
44711         
44712         img.onload = function(){
44713             this.canvasElCtx().drawImage(img, 0, 0);
44714         }.bind(this);
44715         
44716         img.src = img_src;
44717         
44718         this.is_empty = false;
44719     },
44720     
44721     selectImage: function()
44722     {
44723         this.fileEl().dom.click();
44724     },
44725     
44726     uploadImage: function(e)
44727     {
44728         var reader = new FileReader();
44729         
44730         reader.onload = function(e){
44731             var img = new Image();
44732             img.onload = function(){
44733                 this.reset();
44734                 this.canvasElCtx().drawImage(img, 0, 0);
44735             }.bind(this);
44736             img.src = e.target.result;
44737         }.bind(this);
44738         
44739         reader.readAsDataURL(e.target.files[0]);
44740     },
44741     
44742     // Bezier Point Constructor
44743     Point: (function () {
44744         function Point(x, y, time) {
44745             this.x = x;
44746             this.y = y;
44747             this.time = time || Date.now();
44748         }
44749         Point.prototype.distanceTo = function (start) {
44750             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44751         };
44752         Point.prototype.equals = function (other) {
44753             return this.x === other.x && this.y === other.y && this.time === other.time;
44754         };
44755         Point.prototype.velocityFrom = function (start) {
44756             return this.time !== start.time
44757             ? this.distanceTo(start) / (this.time - start.time)
44758             : 0;
44759         };
44760         return Point;
44761     }()),
44762     
44763     
44764     // Bezier Constructor
44765     Bezier: (function () {
44766         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44767             this.startPoint = startPoint;
44768             this.control2 = control2;
44769             this.control1 = control1;
44770             this.endPoint = endPoint;
44771             this.startWidth = startWidth;
44772             this.endWidth = endWidth;
44773         }
44774         Bezier.fromPoints = function (points, widths, scope) {
44775             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44776             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44777             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44778         };
44779         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44780             var dx1 = s1.x - s2.x;
44781             var dy1 = s1.y - s2.y;
44782             var dx2 = s2.x - s3.x;
44783             var dy2 = s2.y - s3.y;
44784             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44785             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44786             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44787             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44788             var dxm = m1.x - m2.x;
44789             var dym = m1.y - m2.y;
44790             var k = l2 / (l1 + l2);
44791             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44792             var tx = s2.x - cm.x;
44793             var ty = s2.y - cm.y;
44794             return {
44795                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44796                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44797             };
44798         };
44799         Bezier.prototype.length = function () {
44800             var steps = 10;
44801             var length = 0;
44802             var px;
44803             var py;
44804             for (var i = 0; i <= steps; i += 1) {
44805                 var t = i / steps;
44806                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44807                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44808                 if (i > 0) {
44809                     var xdiff = cx - px;
44810                     var ydiff = cy - py;
44811                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44812                 }
44813                 px = cx;
44814                 py = cy;
44815             }
44816             return length;
44817         };
44818         Bezier.prototype.point = function (t, start, c1, c2, end) {
44819             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44820             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44821             + (3.0 * c2 * (1.0 - t) * t * t)
44822             + (end * t * t * t);
44823         };
44824         return Bezier;
44825     }()),
44826     
44827     throttleStroke: function(fn, wait) {
44828       if (wait === void 0) { wait = 250; }
44829       var previous = 0;
44830       var timeout = null;
44831       var result;
44832       var storedContext;
44833       var storedArgs;
44834       var later = function () {
44835           previous = Date.now();
44836           timeout = null;
44837           result = fn.apply(storedContext, storedArgs);
44838           if (!timeout) {
44839               storedContext = null;
44840               storedArgs = [];
44841           }
44842       };
44843       return function wrapper() {
44844           var args = [];
44845           for (var _i = 0; _i < arguments.length; _i++) {
44846               args[_i] = arguments[_i];
44847           }
44848           var now = Date.now();
44849           var remaining = wait - (now - previous);
44850           storedContext = this;
44851           storedArgs = args;
44852           if (remaining <= 0 || remaining > wait) {
44853               if (timeout) {
44854                   clearTimeout(timeout);
44855                   timeout = null;
44856               }
44857               previous = now;
44858               result = fn.apply(storedContext, storedArgs);
44859               if (!timeout) {
44860                   storedContext = null;
44861                   storedArgs = [];
44862               }
44863           }
44864           else if (!timeout) {
44865               timeout = window.setTimeout(later, remaining);
44866           }
44867           return result;
44868       };
44869   }
44870   
44871 });
44872
44873  
44874
44875